Documentation ¶
Overview ¶
Package jsonapi is simple wrapper for buildin net/http package. It aims to let developers build json-based web api easier.
Usage ¶
Create an api handler is so easy:
// HelloArgs is data structure for arguments passed by POST body. type HelloArgs struct { Name string Title string } // HelloReply defines data structure this api will return. type HelloReply struct { Message string } // HelloHandler greets user with hello func HelloHandler(q jsonapi.Request) (res interface{}, err error) { // Read json objct from request. var args HelloArgs if err = q.Decode(&args); err != nil { // The arguments are not passed in JSON format, returns http // status 400 with {"errors": [{"detail": "invalid param"}]} err = jsonapi.E400.SetOrigin(err).SetData("invalid param") return } res = HelloReply{fmt.Sprintf("Hello, %s %s", args,Title, args.Name)} return }
And this is how we do in main function:
// Suggested usage apis := []jsonapi.API{ {"/api/hello", HelloHandler}, } jsonapi.Register(http.DefaultMux, apis) // old-school http.Handle("/api/hello", jsonapi.Handler(HelloHandler))
Generated response is a subset of specs in https://jsonapi.org. Refer to `handler_test.go` for examples.
Call API with TypeScript ¶
There's a `fetch.ts` providing `grab<T>()` as simple wrapping around `fetch()`. With following Go code:
type MyStruct struct { X int `json:"x"` Y bool `json:"y" } func MyAPI(q jsonapi.Request) (ret interface{}, err error) { return []MyStruct{ {X: 1, Y: true}, {X: 2}, }, nil } function main() { apis := []jsonapi.API{ {"/my-api", MyAPI}, } jsonapi.Register(http.DefaultMux, apis) http.ListenAndServe(":80", nil) }
You might write TypeScript code like this:
export interface MyStruct { x?: number; y?: boolean; } export function getMyApi(): Promise<MyStruct[]> { return grab<MyStruct[]>('/my-api'); } export function postMyApi(): Promise<MyStruct[]> { return grab<MyStruct[]>('/my-api', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify('my data') }); }
Index ¶
- Variables
- func ConvertCamelToSlash(name string) string
- func ConvertCamelToSnake(name string) string
- func Failed(e1 error, e2 Error) (data interface{}, err error)
- func Register(mux HTTPMux, apis []API)
- func RegisterAll(mux HTTPMux, prefix string, handlers interface{}, ...)
- type API
- type ErrObj
- type Error
- type FakeRequest
- type HTTPMux
- type Handler
- type Middleware
- type Registerer
- type Request
Constants ¶
This section is empty.
Variables ¶
var ( EUnknown = Error{Code: 0, /* contains filtered or unexported fields */} E301 = Error{Code: 301, /* contains filtered or unexported fields */} E302 = Error{Code: 302, /* contains filtered or unexported fields */} E303 = Error{Code: 303, /* contains filtered or unexported fields */} E304 = Error{Code: 304, /* contains filtered or unexported fields */} E307 = Error{Code: 307, /* contains filtered or unexported fields */} E400 = Error{Code: 400, /* contains filtered or unexported fields */} E401 = Error{Code: 401, /* contains filtered or unexported fields */} E403 = Error{Code: 403, /* contains filtered or unexported fields */} E404 = Error{Code: 404, /* contains filtered or unexported fields */} E408 = Error{Code: 408, /* contains filtered or unexported fields */} E409 = Error{Code: 409, /* contains filtered or unexported fields */} E410 = Error{Code: 410, /* contains filtered or unexported fields */} E413 = Error{Code: 413, /* contains filtered or unexported fields */} E415 = Error{Code: 415, /* contains filtered or unexported fields */} E418 = Error{Code: 418, /* contains filtered or unexported fields */} E426 = Error{Code: 426, /* contains filtered or unexported fields */} E429 = Error{Code: 429, /* contains filtered or unexported fields */} E500 = Error{Code: 500, /* contains filtered or unexported fields */} E501 = Error{Code: 501, /* contains filtered or unexported fields */} E502 = Error{Code: 502, /* contains filtered or unexported fields */} E503 = Error{Code: 503, /* contains filtered or unexported fields */} E504 = Error{Code: 504, /* contains filtered or unexported fields */} // application-defined error APPERR = Error{Code: 200} // special error, preventing ServeHTTP method to encode the returned data // // For string, []byte or anything implements fmt.Stringer returned, we will // write it to response as-is. // // For other type, we use fmt.FPrintf(responseWriter, "%v", returnedData). // // You will also have to: // - Set HTTP status code manually. // - Set necessary response headers manually. // - Take care not to be overwritten by middleware. ASIS = Error{Code: -1} )
here are predefined error instances, you should call SetData before use it like
return nil, E404.SetData("User not found")
You might noticed that here's no 500 error. You should just return a normal error instance instead.
return nil, errors.New("internal server error")
Functions ¶
func ConvertCamelToSlash ¶
ConvertCamelToSlash is a helper to convert CamelCase to camel/case
func ConvertCamelToSnake ¶
ConvertCamelToSnake is a helper to convert CamelCase to camel_case
func Failed ¶
Failed wraps you error object and prepares suitable return type to be used in controller
Here's a common usage:
if err := req.Decode(¶m); err != nil { return jsonapi.Failed(err, jsonapi.E400.SetData("invalid parameter")) } if err := param.IsValid(); err != nil { return jsonapi.Failed(err, jsonapi.E400.SetData("invalid parameter")) }
func RegisterAll ¶
func RegisterAll( mux HTTPMux, prefix string, handlers interface{}, converter func(string) string, )
RegisterAll helps you to register all handler methods
As using reflection to do the job, only exported methods with correct signature are registered.
converter is used to convert from method name to url pattern, see CovertCamelToSnake for example.
If converter is nil, name will leave unchanged.
Types ¶
type ErrObj ¶
ErrObj defines how an error is exported to client
For jsonapi.Error, Code will contains result of SetCode; Detail will be SetData
For other error types, only Detail is set, as error.Error()
type Error ¶
type Error struct { Code int Origin error // prepared for application errors // contains filtered or unexported fields }
Error represents an error status of the HTTP request. Used with APIHandler.
func (Error) EqualTo ¶
EqualTo tells if two Error instances represents same kind of error
It compares all fields no matter exported or not, excepts Origin
func (Error) SetData ¶
SetData creates a new Error instance and set the error message or url according to the error code
type FakeRequest ¶
type FakeRequest struct { // this is used to implement Request.Decode() Decoder *json.Decoder // this is used to implement Request.R() and Request.WithValue() Req *http.Request // this is used to implement Request.W() Resp http.ResponseWriter }
FakeRequest implements a Request and let you do some magic in it
func (*FakeRequest) Decode ¶
func (r *FakeRequest) Decode(data interface{}) error
Decode implements Request
func (*FakeRequest) WithValue ¶
func (r *FakeRequest) WithValue(key, val interface{}) (ret Request)
WithValue implements Request
type HTTPMux ¶
HTTPMux abstracts http.ServeHTTPMux, so it will be easier to write tests
Only needed methods are added here.
type Handler ¶
Handler is easy to use entry for API developer.
Just return something, and it will be encoded to JSON format and send to client. Or return an Error to specify http status code and error string.
func myHandler(dec *json.Decoder, httpData *HTTP) (interface{}, error) { var param paramType if err := dec.Decode(¶m); err != nil { return nil, jsonapi.E400.SetData("You must send parameters in JSON format.") } return doSomething(param), nil }
To redirect clients, return 301~303 status code and set Data property
return nil, jsonapi.E301.SetData("http://google.com")
Redirecting depends on http.Redirect(). The data returned from handler will never write to ResponseWriter.
This basically obey the http://jsonapi.org rules:
- Return {"data": your_data} if error == nil
- Return {"errors": [{"code": application-defined-error-code, "detail": message}]} if error returned
type Registerer ¶
type Registerer interface { Register(mux HTTPMux, apis []API) RegisterAll(mux HTTPMux, prefix string, handlers interface{}, conv func(string) string) With(m Middleware) Registerer }
Registerer represents a chain of middleware
With( myMiddleware ).With( apitool.LogIn(apitool.JSONFormat( log.New(os.Stdout, "myapp", log.LstdFlags), )), ).RegisterAll(mux, "/api", myHandler)
Request processing flow will be:
- mux.ServeHTTP
- myMiddleWare
- Logging middleware
- myHandler
- Logging middleware
- myMiddleWare
type Request ¶
type Request interface { // Decode() helps you to read parameters in request body Decode(interface{}) error // R() retrieves original http request R() *http.Request // W() retrieves original http response writer W() http.ResponseWriter // WithValue() adds a new key-value pair in context of http request WithValue(key, val interface{}) Request }
Request represents most used data a handler need
func FromHTTP ¶
func FromHTTP(w http.ResponseWriter, r *http.Request) Request
FromHTTP creates a Request instance from http request and response
func WrapRequest ¶
WrapRequest creates a new Request, with http request replaced
func WrapResponse ¶
func WrapResponse(q Request, w http.ResponseWriter) Request
WrapResponse creates a new Request, with http response replaced
Directories ¶
Path | Synopsis |
---|---|
Package apitest provides few tools helping you write tests
|
Package apitest provides few tools helping you write tests |
Package apitool provides few middlewares helping you create your app
|
Package apitool provides few middlewares helping you create your app |
gorsess
Package gorsess wraps gorilla session to SessionProvider
|
Package gorsess wraps gorilla session to SessionProvider |
sessez
Package sessez provides an easy to use session to work with jsonapi
|
Package sessez provides an easy to use session to work with jsonapi |
sessez/ezmemstore
Package ezmemstore stores session data in memory You have to call GC periodically to release memory.
|
Package ezmemstore stores session data in memory You have to call GC periodically to release memory. |
sessez/ezpgxstore
Package ezpgxstore stores session data in Postgres using pgx You have to call GC() periodically to release db storage space.
|
Package ezpgxstore stores session data in Postgres using pgx You have to call GC() periodically to release db storage space. |