Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to understand rest in go-zero Framework

2025-01-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly introduces "how to understand rest in go-zero framework". In daily operation, I believe many people have doubts about how to understand rest in go-zero framework. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubts about "how to understand rest in go-zero framework". Next, please follow the editor to study!

Service creation

It is very easy to create a http service in go-zero. It is officially recommended to use the goctl tool to generate it. To facilitate the demonstration, you can create the service manually, with the following code

Package mainimport ("log"net/http"github.com/tal-tech/go-zero/core/logx"github.com/tal-tech/go-zero/core/service"github.com/tal-tech/go-zero/rest"github.com/tal-tech/go-zero/rest/httpx") func main () {srv Err: = rest.NewServer (rest.RestConf {Port: 9090, / / listening port ServiceConf: service.ServiceConf {Log: logx.LogConf {Path: ". / logs"}, / / log path} }) if err! = nil {log.Fatal (err)} defer srv.Stop () / / registered routing srv.AddRoutes ([] rest.Route {{Method: http.MethodGet, Path: "/ user/info" Handler: userInfo,},}) srv.Start () / start the service} type User struct {Name string `json: "name" `Addr string `json: "addr" `Level int `json: "level" `} func userInfo (w http.ResponseWriter R * http.Request) {var req struct {UserId int64 `form: "user_id" `/ / definition parameter} if err: = httpx.Parse (r, & req) Err! = nil {/ / parsing parameters httpx.Error (w, err) return} users: = map [int64] * User {1: & User {"go-zero", "shanghai", 1}, 2: & User {"go-queue", "beijing", 2},} httpx.WriteJson (w, http.StatusOK) Users [req.UserId]) / / return result}

Create a service through rest.NewServer. The example configures the port number and log path. After the service starts, it listens on port 9090, creates a logs directory under the current directory, and creates log files of various levels.

Then register the route through srv.AddRoutes, and each route needs to define the route's method, Path, and Handler, where the Handler type is http.HandlerFunc

Finally, start the service through srv.Start. After starting the service, you can see the returned result by visiting http://localhost:9090/user/info?user_id=1.

{name: "go-zero", addr: "shanghai", level: 1}

At this point, a simple http service is created. It can be seen that it is very simple to create a http service using rest. It is mainly divided into three steps: create Server, register routing, and start the service.

JWT authentication

Authentication is a necessary capability for almost every application, and there are many ways of authentication, among which jwt is a relatively simple and reliable one. Jwt authentication is built into the rest framework. The principle flow of jwt is shown below.

jwt

In the rest framework, jwt authentication is enabled through rest.WithJwt (secret), where secret is the server key cannot be disclosed, because you need to use secret to calculate the signature to verify whether the payload has been tampered with. If the secret disclosure client can issue the token itself, hackers can tamper with the token at will. We modify based on the example above to verify how to use jwt authentication in rest

Get jwt

In the first step, the client needs to obtain the jwt and implement the jwt generation logic in the login interface.

Srv.AddRoute (rest.Route {Method: http.MethodPost, Path: "/ user/login", Handler: userLogin,})

For the convenience of demonstration, the logic of userLogin is very simple. The main thing is to get the information and generate jwt, store the obtained information in jwt payload, and then return jwt.

Func userLogin (w http.ResponseWriter, r * http.Request) {var req struct {UserName string `json: "user_name" `UserId int `json: "user_id" `} if err: = httpx.Parse (r, & req) Err! = nil {httpx.Error (w, err) return} token, _: = genToken (accessSecret, map [string] interface {} {"user_id": req.UserId, "user_name": req.UserName,}, accessExpire) httpx.WriteJson (w, http.StatusOK Struct {UserId int `json: "user_id" `UserName string `json: "user_name" `Token string `json: "token" `} {UserId: req.UserId, UserName: req.UserName, Token: token,})}

The method to generate jwt is as follows

Func genToken (secret string, payload map [string] interface {}, expire int64) (string, error) {now: = time.Now () .Unix () claims: = make (jwt.MapClaims) claims ["exp"] = now + expire claims ["iat"] = now for k V: = range payload {claims [k] = v} token: = jwt.New (jwt.SigningMethodHS256) token.Claims = claims return token.SignedString ([] byte (secret))}

Access through cURL after starting the service

Curl-X "POST"http://localhost:9090/user/login"\-H 'Content-Type: application/json; charset=utf-8'\-d $' {" user_name ":" gozero "," user_id ": 666}'

The result will be returned as follows

{"user_id": 666, "user_name": "gozero", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDYxMDgwNDcsImlhdCI6MTYwNTUwMzI0NywidXNlcl9pZCI6NjY2LCJ1c2VyX25hbWUiOiJnb3plcm8ifQ.hhMd5gc3F9xZwCUoiuFqAWH48xptqnNGph0AKVkTmqM"} add Header

Enable jwt authentication through rest.WithJwt (accessSecret)

Srv.AddRoute (rest.Route {Method: http.MethodGet, Path: "/ user/data", Handler: userData,}, rest.WithJwt (accessSecret))

The 401 Unauthorized authentication failed when the access / user/data API is returned. If you add Authorization Header, you can access it normally.

Curl "http://localhost:9090/user/data?user_id=1"\-H 'Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDYxMDgwNDcsImlhdCI6MTYwNTUwMzI0NywidXNlcl9pZCI6NjY2LCJ1c2VyX25hbWUiOiJnb3plcm8ifQ.hhMd5gc3F9xZwCUoiuFqAWH48xptqnNGph0AKVkTmqM' to get information

Generally, the user's information, such as the user's id or user name, will be stored in the payload of jwt, and then our pre-stored information will be parsed from the payload of jwt to know which user initiated this request.

Func userData (w http.ResponseWriter, r * http.Request) {var jwt struct {UserId int `ctx: "user_id" `UserName string `ctx: "user_name" `} err: = contextx.For (r.Context (), & jwt) if err! = nil {httpx.Error (w, err)} httpx.WriteJson (w, http.StatusOK) Struct {UserId int `json: "user_id" `UserName string `json: "user_name" `} {UserId: jwt.UserId, UserName: jwt.UserName,})} implementation principle

Jwt authentication is implemented in authhandler.go, and the implementation principle is relatively simple. First, parse jwt token according to secret to verify whether token is valid. If it is invalid or there is an error in verification, 401 Unauthorized is returned.

Func unauthorized (w http.ResponseWriter, r * http.Request, err error, callback UnauthorizedCallback) {writer: = newGuardedResponseWriter (w) if err! = nil {detailAuthLog (r, err.Error ())} else {detailAuthLog (r, noDetailReason)} if callback! = nil {callback (writer, r, err)} writer.WriteHeader (http.StatusUnauthorized)}

After verification, the information in payload is stored in the context of http request.

Ctx: = r.Context () for k, v: = range claims {switch k {case jwtAudience, jwtExpire, jwtId, jwtIssueAt, jwtIssuer, jwtNotBefore, jwtSubject: / / ignore the standard claims default: ctx = context.WithValue (ctx, k, v)}} next.ServeHTTP (w, r.WithContext (ctx)) middleware

Middleware in web framework is a way to decouple business and non-business functions. In web framework, we can use middleware to achieve functions such as authentication, current limiting, circuit breaker and so on. The principle flow of middleware is shown below.

There are very rich middleware built into the rest framework. Under the rest/handler path, all middleware are linked through alice tools. When a request is made, it will go through each middleware in turn. When all the conditions are met, the final request will reach the real business Handler to execute business logic. The jwt authentication described above is realized through authHandler. Since there are many built-in middleware, space is limited and can not be introduced one by one, interested partners can learn by themselves. Here we introduce the middleware PromethousHandler for prometheus index collection. The code is as follows.

Func PromethousHandler (path string) func (http.Handler) http.Handler {return func (next http.Handler) http.Handler {return http.HandlerFunc (func (w http.ResponseWriter) R * http.Request) {startTime: = timex.Now () / / start time cw: = & security.WithCodeResponseWriter {Writer: W} defer func () {/ / time-consuming metricServerReqDur.Observe (int64 (timex.Since (startTime) / time.Millisecond) Path) / / code code metricServerReqCodeTotal.Inc (path, strconv.Itoa (cw.Code))} () next.ServeHTTP (cw, r)})}}

In this middleware, the start time is recorded at the beginning of the request, and after the end of the request, the time spent and the returned code code of the current request path are recorded in defer through the Histogram and Counter data types of prometheus, respectively. At this time, we can view the relevant metric information by visiting http://127.0.0.1:9101/metrics.

Routing principle

In the rest framework, routes are registered through the AddRoutes method. Each Route has three attributes: Method, Path and Handler. The Handler type is http.HandlerFunc. The added route will be replaced by featuredRoutes and defined as follows.

FeaturedRoutes struct {priority bool / / priority jwt jwtSetting / / jwt configuration signature signatureSetting / / signature configuration routes [] Route / / routes added through AddRoutes}

FeaturedRoutes is added to the routes attribute of engine through the AddRoutes of engine

Func (s * engine) AddRoutes (r featuredRoutes) {s.routes = append (s.routes, r)}

After calling the Start method to start the service, the Start method of engine is called, and then the StartWithRouter method is called, which is routed through bindRoutes binding

Func (s * engine) bindRoutes (router httpx.Router) error {metrics: = s.createMetrics () for _, fr: = range s.routes {if err: = s.bindFeaturedRoutes (router, fr, metrics); err! = nil {/ / bind route return err}} return nil}

Finally, the Handle method of patRouter will be called for binding, and patRouter implements the Router interface.

Each request method in patRouter corresponds to a tree structure. Each tree node has two attributes: item is the handler corresponding to path, while children is the tree node with and without path parameters, which is defined as follows:

Node struct {item interface {} children [2] map [string] * node} Tree struct {root * node}

Register different path and corresponding handler to the tree through Tree's Add method. We use a diagram to show the storage structure of the tree. For example, we define the route as follows

{Method: http.MethodGet, Path: "/ user", Handler: userHander,}, {Method: http.MethodGet, Path: "/ user/infos", Handler: infosHandler,}, {Method: http.MethodGet, Path: "/ user/info/:id", Handler: infoHandler,}

The tree structure of the routing storage is as follows

When the request comes, the ServeHTTP method of patRouter is called. In this method, the corresponding handler is found through the tree.Search method to execute, otherwise the logic of notFound or notAllow will be executed.

Func (pr * patRouter) ServeHTTP (w http.ResponseWriter, r * http.Request) {reqPath: = path.Clean (r.URL.Path) if tree, ok: = pr.trees [r.Method]; ok {if result, ok: = tree.Search (reqPath) Ok {/ / search the tree for the corresponding handler if len (result.Params) > 0 {r = context.WithPathVars (r, result.Params)} result.Item. (http.Handler) .ServeHTTP (w R) return}} allow, ok: = pr.methodNotAllowed (r.Method, reqPath) if! ok {pr.handleNotFound (w, r) return} if pr.notAllowed! = nil {pr.notAllowed.ServeHTTP (w) R)} else {w.Header () .Set (allowHeader, allow) w.WriteHeader (http.StatusMethodNotAllowed)} so far The study on "how to understand rest in go-zero Framework" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report