Convert Go func to http.HandleFunc that handle json request and response json
- New Status Code Error
- To Handler Func
- Type Config
- Type Req
- Type Resp
- Type Response Error
- Type Status Code Error
funcNewStatusCodeError(codeint, innerErrorerror) (errerror)NewStatusCodeError for returning an error with http code
funcToHandlerFunc(funcs...interface{}) http.HandlerFuncToHandlerFunc convert any go func to a http.HandleFunc, that will accept json.Unmarshal request body as parameters, and response with a body with a return values into json.
The second argument is an arguments injector, it's parameter should be (w http.ResponseWriter, r *http.Request), and return values Will be injected to first func's first few arguments.
varhelloworld=func(namestring, genderint) (rstring, errerror) {
ifgender==1 {
r=fmt.Sprintf("Hi, Mr. %s", name)
} elseifgender==2 {
r=fmt.Sprintf("Hi, Mrs. %s", name)
} else {
err=fmt.Errorf("Sorry, I don't know about your gender.")
}
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
responseBody:=httpPostJSON(hf, ` {"params": [ "Gates", 1 ]} `)
fmt.Println(responseBody)
responseBody=httpPostJSON(hf, ` {"params": [ "Gates", 2 ]} `)
fmt.Println(responseBody)
responseBody=httpPostJSON(hf, ` {"params": [ "Gates", 3 ]} `)
fmt.Println(responseBody)
//Output:// {"results":["Hi, Mr. Gates",null]}//// {"results":["Hi, Mrs. Gates",null]}//// {"results":["",{"error":"Sorry, I don't know about your gender.","value":{}}]}varhelloworld=func(namestring, pstruct {
NamestringAddressstruct {
ZipcodeintAddress1string
}
}) (rstring, errerror) {
r=fmt.Sprintf("Hi, Mr. %s, Your zipcode is %d", name, p.Address.Zipcode)
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
responseBody:=httpPostJSON(hf, ` {"params": [ "Felix", { "Address": { "Zipcode": 100 } } ]} `)
fmt.Println(responseBody)
//Output:// {"results":["Hi, Mr. Felix, Your zipcode is 100",null]}varhelloworld=func(
names []string,
genderOfNamesmap[string]string,
p*struct {
Names []stringAddressstruct {
ZipcodeintAddress1string
}
},
pointerNames*[]string,
) (rstring, errerror) {
r=fmt.Sprintf("Hi, Mr. %s, Your zipcode is %d, Your gender is %s", names[0], p.Address.Zipcode, genderOfNames[names[0]])
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
responseBody:=httpPostJSON(hf, `{"params":[ ["Felix"] ]}`)
fmt.Println(responseBody)
responseBody=httpPostJSON(hf, ` {"params": [ ["Felix", "Gates"], { "Felix": "Male", "Gates": "Male" }, { "Names": ["F1", "F2"], "Address": { "Zipcode": 100 } }, ["p1", "p2"] ]} `)
fmt.Println(responseBody)
responseBody=httpPostJSON(hf, ``)
fmt.Println(responseBody)
//Output:// {"results":["",{"error":"require 4 params, but passed in 1 params","value":{}}]}//// {"results":["Hi, Mr. Felix, Your zipcode is 100, Your gender is Male",null]}//// {"results":["",{"error":"decode request params error","value":{}}]}4) First context: If first parameter is a context.Context, It will be passed in with request.Context()
varhelloworld=func(ctx context.Context, namestring) (rstring, errerror) {
userid:=ctx.Value("userid").(string)
r=fmt.Sprintf("Hello %s, My user id is %s", name, userid)
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
middleware:=func(inner http.HandlerFunc) http.HandlerFunc {
returnfunc(w http.ResponseWriter, r*http.Request) {
r=r.WithContext(context.WithValue(r.Context(), "userid", "123"))
inner(w, r)
}
}
responseBody:=httpPostJSON(middleware(hf), `{"params": [ "Hello" ]}`)
fmt.Println(responseBody)
//Output:// {"results":["Hello Hello, My user id is 123",null]}varhelloworld=func(namestring, genderint) (rstring, errerror) {
err=&complicatedError{ErrorCode: 8800, ErrorDeepReason: "It crashed."}
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
responseBody:=httpPostJSON(hf, ` {"params": [ "Gates", 1 ]} `)
fmt.Println(responseBody)
//Output:// {"results":["",{"error":"It crashed.","value":{"ErrorCode":8800,"ErrorDeepReason":"It crashed."}}]}varhelloworld=func(ctx context.Context) (rstring, errerror) {
r="Done"return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
ts:=httptest.NewServer(hf)
deferts.Close()
res, err:=http.Get(ts.URL)
iferr!=nil {
log.Fatal(err)
}
b, _:=ioutil.ReadAll(res.Body)
res.Body.Close()
fmt.Println(string(b))
//Output:// {"results":["Done",null]}7) Use NewStatusCodeError or implement StatusCodeError interface to set http status code of response.
varhelloworld=func(namestring, genderint) (rstring, errerror) {
err=jsonhandlerfunc.NewStatusCodeError(http.StatusForbidden, fmt.Errorf("you can't access it"))
return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld)
responseBody, code:=httpPostJSONReturnCode(hf, ` {"params": [ "Gates", 1 ]} `)
fmt.Println(code)
fmt.Println(responseBody)
//Output:// 403// {"results":["",{"error":"you can't access it","value":{}}]}the argument injector parameters should be func(w http.ResponseWriter, r *http.Request)
the return values except the last error will be passed to the first func.
varhelloworld=func(cartIdint, userIdstring, namestring, genderint) (rstring, errerror) {
r=fmt.Sprintf("cardId: %d, userId: %s, name: %s, gender: %d", cartId, userId, name, gender)
return
}
varargsInjector=func(w http.ResponseWriter, r*http.Request) (cartIdint, userIdstring, errerror) {
cartId=20userId="100"return
}
hf:=jsonhandlerfunc.ToHandlerFunc(helloworld, argsInjector)
responseBody, code:=httpPostJSONReturnCode(hf, ` {"params": [ "Gates", 2 ]} `)
fmt.Println(code)
fmt.Println(responseBody)
varargsInjectorWithError=func(w http.ResponseWriter, r*http.Request) (cartIdint, userIdstring, errerror) {
err=jsonhandlerfunc.NewStatusCodeError(http.StatusForbidden, fmt.Errorf("you can't access it"))
return
}
hf=jsonhandlerfunc.ToHandlerFunc(helloworld, argsInjectorWithError)
responseBody, code=httpPostJSONReturnCode(hf, ` {"params": [ "Gates", 2 ]} `)
fmt.Println(code)
fmt.Println(responseBody)
// You can pass more injectors to addup provide arguments from beginning.varcardItInjector=func(w http.ResponseWriter, r*http.Request) (cartIdint, errerror) {
cartId=30return
}
varuserIdInjecter=func(w http.ResponseWriter, r*http.Request) (userIdstring, errerror) {
userId="300"return
}
hf=jsonhandlerfunc.ToHandlerFunc(helloworld, cardItInjector, userIdInjecter)
responseBody, code=httpPostJSONReturnCode(hf, ` {"params": [ "Gates", 2 ]} `)
fmt.Println(code)
fmt.Println(responseBody)
// You can also pass only one injector without main funchf=jsonhandlerfunc.ToHandlerFunc(cardItInjector)
responseBody, code=httpPostJSONReturnCode(hf, "")
fmt.Println(code)
fmt.Println(responseBody)
//Output:// 200// {"results":["cardId: 20, userId: 100, name: Gates, gender: 2",null]}//// 403// {"results":["",{"error":"you can't access it","value":{}}]}//// 200// {"results":["cardId: 30, userId: 300, name: Gates, gender: 2",null]}//// 200// {"results":[30,null]}deferfunc() {
ifr:=recover(); r!=nil {
fmt.Println(r)
}
}()
varinj=func(w http.ResponseWriter, r*http.Request) (a*http.Request, bfloat64, cstring, errerror) {
return
}
varf=func(a, b, cstring) (errerror) {
return
}
jsonhandlerfunc.ToHandlerFunc(f, inj)
fmt.Println("DONE")
//Output:// func(string, string, string) error params type is [string string string], but injecting [*http.Request float64 string]varconfidentialErr=fmt.Errorf("Internal error, contains confidential information, should not exposed")
varerrMapping=map[error]error{
confidentialErr: errors.New("system error"),
}
cfg:=&jsonhandlerfunc.Config{
ErrHandler: func(oldErrerror) (newErrerror) {
returnerrMapping[oldErr]
},
}
varhelloworld=func(namestring, genderint) (rstring, errerror) {
err=confidentialErrreturn
}
hf:=cfg.ToHandlerFunc(helloworld)
responseBody:=httpPostJSON(hf, ` {"params": [ "Gates", 1 ]} `)
fmt.Println(responseBody)
//Output:// {"results":["",{"error":"system error","value":{}}]}typeConfigstruct {
ErrHandlerfunc(oldErrerror) (newErrerror)
}func (cfg*Config) ToHandlerFunc(funcs...interface{}) http.HandlerFunctypeReqstruct {
Paramsinterface{} `json:"params"`
}typeRespstruct {
Resultsinterface{} `json:"results"`
}typeResponseErrorstruct {
Errorstring`json:"error,omitempty"`Valueinterface{} `json:"value,omitempty"`
}ResponseError is error of the Go func return values will be wrapped with this struct, So that error details can be exposed as json.
typeStatusCodeErrorinterface {
StatusCode() int
}StatusCodeError for the error you returned contains a StatusCode method, It will be set to to http response.