HTTP Extensions
Overview
Currently go-zero provides very strong http ability, but some features are still not go-zero implemented, there is a x repository dedicated to go-zero, where HTTP extensions are supported:
- Code-data response format support
- xml response support
- code-msg error type support
For more information, see https://github.com/zeroicro/x
code-data uniform response format usage
In many cases, in order to achieve a uniform response format with the front end, we usually encapsulate a layer of business code, msg and business data in the following common format:
{
"code": 0,
"msg": "ok",
"data": {
...
}
}
There are currently two approaches if this format response is required:
- Custom response format
- Use go-zero extension to implement
We show below with a go-zero extension package.
- Initialize a demo project
$ mkdir demo && cd demo
$ go mod init demo
- Create an api file in demo directory
user.api
, add the following
syntax = "v1"
type LoginRequest {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResponse {
UID int64 `json:"uid"`
Name string `json:"name"`
}
service user {
@handler login
post /user/login (LoginRequest) returns (LoginResponse)
}
- Generate code by goctl
$ goctl api go --api user.api --dir .
Done.
- Add stock logic, modify
demo/internal/logic/loginlogic.go
file to make its code:
package logic
import (
"context"
"demo/internal/svc"
"demo/internal/types"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/x/errors"
)
type LoginLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
return &LoginLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
// mock login
if req.Username != "go-zero" || req.Password != "123456" {
return nil, errors.New(1001, "invalid username or password")
}
resp = new(types.LoginResponse)
resp.Name = "go-zero"
resp.UID = 1
return resp, nil
}
So let’s first look at the format of the response before the file demo/internal/handler/loginhandler.go
:
$ cd demo
$ go mod tidy
$ go run user.go
# before
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"123456"
}'
{"uid":1,"name":"go-zero"}
# after
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"111111"
}'
code: 1001, msg: invalid username or password
- Next, we modify
demo/internal/handler/loginhandler.go
file, replacing the response method inloginHandler
with the method in the extension pack
::tip tip tips
This step can be implemented by modifying templates to prevent every time they are generated, custom templates can be referenced Template Customization :::
package handler
import (
"net/http"
"demo/internal/logic"
"demo/internal/svc"
"demo/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
xhttp "github.com/zeromicro/x/http"
)
func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := logic.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
if err != nil {
// code-data response
xhttp.JsonBaseResponseCtx(r.Context(), w, err)
} else {
// code-data response
xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
}
}
}
Restart user to request a response format:
$ go run user.go
# no error case
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"123456"
}'
{"code":0,"msg":"ok","data":{"uid":1,"name":"go-zero"}}
# error case
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"111111"
}'
{"code":1001,"msg":"invalid username or password"}
xml response support
Further to the above, we will change the response from demo/internal/handler/loginhandler.go
to x http.xhttp.OkXml
or OkXmlCtx
if the same code-data response format is required, exchange x http. mlBaseResponse
or x http.XmlBaseResponseCtx
For example, we revised the following code: as an example: x http.XmlBaseResponseCtx
package handler
import (
"net/http"
"demo/internal/logic"
"demo/internal/svc"
"demo/internal/types"
"github.com/zeromicro/go-zero/rest/httpx"
xhttp "github.com/zeromicro/x/http"
)
func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req types.LoginRequest
if err := httpx.Parse(r, &req); err != nil {
httpx.ErrorCtx(r.Context(), w, err)
return
}
l := logic.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
if err != nil {
//xhttp.XmlBaseResponse(w,err)
xhttp.XmlBaseResponseCtx(r.Context(),w,err)
} else {
//xhttp.XmlBaseResponse(w,resp)
xhttp.XmlBaseResponseCtx(r.Context(),w,resp)
}
}
}
We rerun user program to see the response format:
$ go run user.go
# no error
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"123456"
}'
<xml version="1.0" encoding="UTF-8"><code>0</code><msg>ok</msg><data><UID>1</UID><Name>go-zero</Name></data></xml>
# error case
curl --location '127.0.0.1:8888/user/login' \
--header 'Content-Type: application/json' \
--data '{
"username":"go-zero",
"password":"111111"
}'
<xml version="1.0" encoding="UTF-8"><code>1001</code><msg>invalid username or password</msg></xml>