Rocket’s handler contains two parts.
Basically, we have the creator for handler. It uses like:
package example
import (
"github.com/dannypsnl/rocket"
)
func handler() string { return "" }
func main() {
// In `Mount`
rocket.Get("/hello", handler)
}
Now we have a handler function handler
, and we can use a route "/hello
and handler
to create a new Rocket’s handler.
When request path matches this route, the response is response of handler function.
We have following creator mapping to HTTP method currently.
Get
HTTP Method GETPost
HTTP Method POSTPut
HTTP Method PUTPatch
HTTP Method PATCHDelete
HTTP Method DELETENow you already know how to create handler has different method. Let’s look the most interesting feature of rocket User-defined Context
So the question is, how to have one in rocket?
package example
type User struct {
Name string `route:"name"`
Age uint64 `route:"age"`
}
Don’t be surprised, that’s all, and then we use the type you create as parameter of your handle function
package example
import (
"github.com/dannypsnl/rocket"
)
func main() {
// In `Mount`
rocket.Get("/:name/:age", func (u *User) string {
return "Hello " + u.Name + ", your age is " + strconv.FormatUint(u.Age, 10)
})
}
Ok, we know how to use the field of context, but where is it came from?
Let’s return to "/:name/:age"
, this is how we fill your context, in variant route, what you defined as :key
things, will be the value of tag route:"key"
At here, we got route:"name"
& route:"age"
, so request path Danny/21
will let your context got string Danny
& uint64 21
p.s. type of Age
this field is uint64
, so we will try to parsing the value of request path.
If it’s not an uint64
, then we return HTTP Status Code: 400.
But just route
? Nope, we also have:
query:"key"
for request path /path/to/route?key=value
form:"key"
for FORM request
multiform:"key"
for multiple forms request
multiform:"key" file:"yes"
for multiple forms request, and it’s a file. In this case, the type of field must be io.ReadCloser
.
json:"key"
for request body is JSON(here has some problem, we can also handle GET method just need JSON body,
this is a bug, it should only work with POST, PUT, PATCH with application/json)
header:"key"
, to getting header like Content-Type
, Authorization
cookie:"key"
, a very important fact of cookie tag is it only accept you use *http.Cookie
as field type,
e.g.
package example
import "net/http"
type UserToken struct {
token *http.Cookie `cookie:"token"`
}
and it would get the most matched cookie, so avoid using duplicate cookie name in your application would be better, another important thing is if no cookie matched, this tag won’t follow the optional contract, so don’t use cookie tag in a general purpose context would help you avoid Bad Request
We still have one thing haven’t been mentioned, optional field, just like its name, it allowed you omit the field and won’t cause HTTP Status Code: 400.
Example context definition:
type Transaction struct {
Amount uint64 `form:"amount"`
Canceled *bool `form:"canceled"`
}
At here, the field Canceled
would be nil
if you didn’t give it a value.
Multiple contexts allows you put more than one contexs in your handler function, this make you can reuse more contexts, for example, here is a proxy of kubernetes List API:
import (
"net/http"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
scheme "k8s.io/client-go/kubernetes/scheme"
"github.com/dannypsnl/rocket"
"github.com/dannypsnl/rocket/response"
)
type (
CheckWatch struct {
IsWatch *bool `query:"watch"`
}
Resource struct {
Namespace string `route:"namespace"`
Kind string `route:"kind"`
}
)
// ignore all errors
func kubernetesProxy(c *CheckWatch, r *Resource) response.Json {
opts := &metav1.ListOptions{}
if c.IsWatch != nil && *c.IsWatch {
return response.Stream(func(w http.ResponseWriter) {
opts.Watch = true
watchInterface, err := kubeClient.CoreV1().RESTClient().Get().
Namespace(r.Namespace).
Resource(r.Kind).
VersionedParams(opts, scheme.ParameterCodec).
Watch()
for {
select {
case event := <- watchInterface.ResultChan():
data, err := json.Marshal(event.Object)
w.Write(data)
}
}
}).
Headers(response.Headers{
"Content-Type": "application/json",
})
}
// We should create result by Kind actually, but just let me use hard code here as an example
result := &corev1.EndpointsList{}
// ignore how to initialize kubeClient
err := kubeClient.CoreV1().RESTClient().Get().
Namespace(r.Namespace).
Resource(r.Kind).
VersionedParams(opts, scheme.ParameterCodec).
Do().
Into(result)
data, err := json.Marshal(result)
return response.Json(data)
}
rocket.
// ignore
Mount(
rocket.Get("/api/v1/namespaces/:namespace/:kind", kubernetesProxy),
).
Launch()