Go web gin

Go web gin

gin golang web crash json node koa

go get -u github.com/gin-gonic/gin

json

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/someJson", func(c *gin.Context) { data := map[string]interface{}{ "lang": "go lang", "tag": "<br>", } c.JSON(http.StatusOK, data) }) r.Run(":8000") }

gin.Default logger recovery New Use

New

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.New() r.Use(gin.Logger(), gin.Recovery()) r.GET("/someJson", func(c *gin.Context) { data := map[string]interface{}{ "lang": "go lang", "tag": "<br>", } c.JSON(http.StatusOK, data) }) r.Run(":8000") }

Gin get post patch delete put options head any any get post patch delete put options head 7 gin http gin RouterGroup

Handle group.handle Handle httpMethod

r.GET Handle

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.New() r.Use(gin.Logger(), gin.Recovery()) r.Handle(http.MethodGet, "/someJson", func(c *gin.Context) { data := map[string]interface{}{ "lang": "go lang", "tag": "<br>", } c.JSON(http.StatusOK, data) }) r.Run(":8000") }

api gin Group

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() v1 := r.Group("/api/v1") v1.GET("/getUser", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "name": "golang", "id": "1", }) }) r.Run(":8000") }

Group RouterGroup routePath basePath

handle calculateAbsolutePath

Gin httprouter koa :param context Param

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user/:id", func(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, gin.H{ "user": id, }) }) r.Run(":8000") }

c.Params.ByName c.Params slice

*

package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/article/*id", func(c *gin.Context) { id := c.Param("id") c.JSON(http.StatusOK, gin.H{ "article": id, }) }) r.Run(":8000") }

/aritcle/123 /article/123/info /aritcle/123/author/info

Get Post

get query string post http body gin Context Query get

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { id := c.Query("id") c.JSON(http.StatusOK, gin.H{ "id": id, }) }) r.Run(":8000") }

context GetQuery

query gin map map map[string][]string context c.Request.URL.Query()

gin parseQuery string key string map

query DefaultQuery GetQuery

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { id := c.DefaultQuery("id", "456") c.JSON(http.StatusOK, gin.H{ "id": id, }) }) r.Run(":8000") }

post form-data http body gin PostForm

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.POST("/user", func(c *gin.Context) { id := c.PostForm("id") c.JSON(http.StatusOK, gin.H{ "code": 200, "id": id, }) }) r.Run(":8000") }

query DefaultPostForm GetPostForm

id := c.DefaultPostForm("id", "456")

cookie http header

cookie c.Cookie cookie c.SetCookie cookie

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { session, _ := c.Cookie("session") c.SetCookie("site_cookie", "cookie1", 3600, "/", "localhost", false, true) c.JSON(http.StatusOK, gin.H{ "code": 200, "session": session, }) }) r.Run(":8000") }

cookie c.Request.Cookie c.Request.Cookie http cookie

Http header c.GetHeader(key)

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { lang := c.GetHeader("lang") c.JSON(http.StatusOK, gin.H{ "code": 200, "lang": lang, }) }) r.Run(":8000") }

request header.get c.Request.Header.Get

HTTP Header

c.Header("user", "golang")

http Header struct

gin context Redirect context request url context

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/info", func(c *gin.Context) { c.Redirect(http.StatusMovedPermanently, "/user") }) r.GET("/article", func(c *gin.Context) { c.Request.URL.Path = "/user" r.HandleContext(c) }) r.GET("/user", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 200, "data": "user", }) }) r.Run(":8000") }

Redirect http Redirect

web gin Static StaticFS StaticFile

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.Static("/", "./public") r.StaticFile("/", "./public") r.StaticFS("/", http.Dir("./public")) r.Run(":8000") }

Static StaticFS

golang http/template gin gin LoadHTMLGlob c.HTML gin render/html.go

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.LoadHTMLGlob("./template/*") r.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.tmpl", gin.H{ "title": "golang", }) }) r.Run(":8000") }
<html> <h1> {{ .title }} </h1> </html>

Context

gin context http koa context context gin

/Context is the most important part of gin. It allows us to pass variables between middleware, //manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { writermem responseWriter Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 fullPath string engine *Engine params *Params //This mutex protect Keys map mu sync.RWMutex //Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} //Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs //Accepted defines a list of manually accepted formats for content negotiation. Accepted []string //queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() queryCache url.Values //formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, //or PUT body parameters. formCache url.Values //SameSite allows a server to define a cookie attribute making it impossible for //the browser to send this cookie along with cross-site requests. sameSite http.SameSite }

query postForm param header cookie context context Get Set context c.Keys

package main import ( "github.com/gin-gonic/gin" "net/http" ) func main() { r := gin.Default() r.GET("/user", func(c *gin.Context) { c.Set("key", "value") val, _ := c.Get("key") c.JSON(http.StatusOK, gin.H{ "code": 200, "key": val, }) }) r.Run(":8000") }

Get gin MustGet panic GetString GetBool GetInt

/************************************/ /******** METADATA MANAGEMENT********/ /************************************/ func (c *Context) Set(key string, value interface{}) {} //Get returns the value for the given key, ie: (value, true). //If the value does not exists it returns (nil, false) func (c *Context) Get(key string) (value interface{}, exists bool) {} //MustGet returns the value for the given key if it exists, otherwise it panics. func (c *Context) MustGet(key string) interface{} {} //GetString returns the value associated with the key as a string. func (c *Context) GetString(key string) (s string) {} //GetBool returns the value associated with the key as a boolean. func (c *Context) GetBool(key string) (b bool) {} //GetInt returns the value associated with the key as an integer. func (c *Context) GetInt(key string) (i int) {} //GetInt64 returns the value associated with the key as an integer. func (c *Context) GetInt64(key string) (i64 int64) {} //GetUint returns the value associated with the key as an unsigned integer. func (c *Context) GetUint(key string) (ui uint) { } //GetUint64 returns the value associated with the key as an unsigned integer. func (c *Context) GetUint64(key string) (ui64 uint64) {} //GetFloat64 returns the value associated with the key as a float64. func (c *Context) GetFloat64(key string) (f64 float64) {} //GetTime returns the value associated with the key as time. func (c *Context) GetTime(key string) (t time.Time) {} //GetDuration returns the value associated with the key as a duration. func (c *Context) GetDuration(key string) (d time.Duration) {} //GetStringSlice returns the value associated with the key as a slice of strings. func (c *Context) GetStringSlice(key string) (ss []string) {} //GetStringMap returns the value associated with the key as a map of interfaces. func (c *Context) GetStringMap(key string) (sm map[string]interface{}) {} //GetStringMapString returns the value associated with the key as a map of strings. func (c *Context) GetStringMapString(key string) (sms map[string]string) {} //GetStringMapStringSlice returns the value associated with the key as a map to a slice of strings. func (c *Context) GetStringMapStringSlice(key string) (smss map[string][]string) {}

gin query postForm param Input Data gin query postForm param

/************************************/ /************ INPUT DATA ************/ /************************************/ //Param returns the value of the URL param. //It is a shortcut for c.Params.ByName(key) // router.GET("/user/:id", func(c *gin.Context) { // //a GET request to/user/john // id := c.Param("id")//id == "john" // }) func (c *Context) Param(key string) string { return c.Params.ByName(key) } //Query returns the keyed url query value if it exists, //otherwise it returns an empty string `("")`. //It is shortcut for `c.Request.URL.Query().Get(key)` // GET/path?id=1234&name=Manu&value= // c.Query("id") == "1234" // c.Query("name") == "Manu" // c.Query("value") == "" // c.Query("wtf") == "" func (c *Context) Query(key string) string { value, _ := c.GetQuery(key) return value } //DefaultQuery returns the keyed url query value if it exists, //otherwise it returns the specified defaultValue string. //See: Query() and GetQuery() for further information. // GET/?name=Manu&lastname= // c.DefaultQuery("name", "unknown") == "Manu" // c.DefaultQuery("id", "none") == "none" // c.DefaultQuery("lastname", "none") == "" func (c *Context) DefaultQuery(key, defaultValue string) string { if value, ok := c.GetQuery(key); ok { return value } return defaultValue } //GetQuery is like Query(), it returns the keyed url query value //if it exists `(value, true)` (even when the value is an empty string), //otherwise it returns `("", false)`. //It is shortcut for `c.Request.URL.Query().Get(key)` // GET/?name=Manu&lastname= // ("Manu", true) == c.GetQuery("name") // ("", false) == c.GetQuery("id") // ("", true) == c.GetQuery("lastname") func (c *Context) GetQuery(key string) (string, bool) { if values, ok := c.GetQueryArray(key); ok { return values[0], ok } return "", false } //QueryArray returns a slice of strings for a given query key. //The length of the slice depends on the number of params with the given key. func (c *Context) QueryArray(key string) []string { values, _ := c.GetQueryArray(key) return values } func (c *Context) initQueryCache() { if c.queryCache == nil { if c.Request != nil { c.queryCache = c.Request.URL.Query() } else { c.queryCache = url.Values{} } } } //GetQueryArray returns a slice of strings for a given query key, plus //a boolean value whether at least one value exists for the given key. func (c *Context) GetQueryArray(key string) ([]string, bool) { c.initQueryCache() if values, ok := c.queryCache[key]; ok && len(values) > 0 { return values, true } return []string{}, false } //QueryMap returns a map for a given query key. func (c *Context) QueryMap(key string) map[string]string { dicts, _ := c.GetQueryMap(key) return dicts } //GetQueryMap returns a map for a given query key, plus a boolean value //whether at least one value exists for the given key. func (c *Context) GetQueryMap(key string) (map[string]string, bool) { c.initQueryCache() return c.get(c.queryCache, key) } //PostForm returns the specified key from a POST urlencoded form or multipart form //when it exists, otherwise it returns an empty string `("")`. func (c *Context) PostForm(key string) string { value, _ := c.GetPostForm(key) return value } //DefaultPostForm returns the specified key from a POST urlencoded form or multipart form //when it exists, otherwise it returns the specified defaultValue string. //See: PostForm() and GetPostForm() for further information. func (c *Context) DefaultPostForm(key, defaultValue string) string { if value, ok := c.GetPostForm(key); ok { return value } return defaultValue } //GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded //form or multipart form when it exists `(value, true)` (even when the value is an empty string), //otherwise it returns ("", false). //For example, during a PATCH request to update the user's email: // email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email")//set email to "mail@example.com" // email= --> ("", true) := GetPostForm("email")//set email to "" // --> ("", false) := GetPostForm("email")//do nothing with email func (c *Context) GetPostForm(key string) (string, bool) { if values, ok := c.GetPostFormArray(key); ok { return values[0], ok } return "", false } //PostFormArray returns a slice of strings for a given form key. //The length of the slice depends on the number of params with the given key. func (c *Context) PostFormArray(key string) []string { values, _ := c.GetPostFormArray(key) return values } func (c *Context) initFormCache() { if c.formCache == nil { c.formCache = make(url.Values) req := c.Request if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { if err != http.ErrNotMultipart { debugPrint("error on parse multipart form array: %v", err) } } c.formCache = req.PostForm } } //GetPostFormArray returns a slice of strings for a given form key, plus //a boolean value whether at least one value exists for the given key. func (c *Context) GetPostFormArray(key string) ([]string, bool) { c.initFormCache() if values := c.formCache[key]; len(values) > 0 { return values, true } return []string{}, false } //PostFormMap returns a map for a given form key. func (c *Context) PostFormMap(key string) map[string]string { dicts, _ := c.GetPostFormMap(key) return dicts } //GetPostFormMap returns a map for a given form key, plus a boolean value //whether at least one value exists for the given key. func (c *Context) GetPostFormMap(key string) (map[string]string, bool) { c.initFormCache() return c.get(c.formCache, key) } //get is an internal method and returns a map which satisfy conditions. func (c *Context) get(m map[string][]string, key string) (map[string]string, bool) { dicts := make(map[string]string) exist := false for k, v := range m { if i := strings.IndexByte(k, '['); i >= 1 && k[0:i] == key { if j := strings.IndexByte(k[i+1:], ']'); j >= 1 { exist = true dicts[k[i+1:][:j]] = v[0] } } } return dicts, exist } //FormFile returns the first file for the provided form key. func (c *Context) FormFile(name string) (*multipart.FileHeader, error) { if c.Request.MultipartForm == nil { if err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil { return nil, err } } f, fh, err := c.Request.FormFile(name) if err != nil { return nil, err } f.Close() return fh, err } //MultipartForm is the parsed multipart form, including file uploads. func (c *Context) MultipartForm() (*multipart.Form, error) { err := c.Request.ParseMultipartForm(c.engine.MaxMultipartMemory) return c.Request.MultipartForm, err } //SaveUploadedFile uploads the form file to specific dst. func (c *Context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { src, err := file.Open() if err != nil { return err } defer src.Close() out, err := os.Create(dst) if err != nil { return err } defer out.Close() _, err = io.Copy(out, src) return err } //Bind checks the Content-Type to select a binding engine automatically, //Depending the "Content-Type" header different bindings are used: // "application/json" --> JSON binding // "application/xml" --> XML binding //otherwise --> returns an error. //It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. //It decodes the json payload into the struct specified as a pointer. //It writes a 400 error and sets Content-Type header "text/plain" in the response if input is not valid. func (c *Context) Bind(obj interface{}) error { b := binding.Default(c.Request.Method, c.ContentType()) return c.MustBindWith(obj, b) } //BindJSON is a shortcut for c.MustBindWith(obj, binding.JSON). func (c *Context) BindJSON(obj interface{}) error { return c.MustBindWith(obj, binding.JSON) } //BindXML is a shortcut for c.MustBindWith(obj, binding.BindXML). func (c *Context) BindXML(obj interface{}) error { return c.MustBindWith(obj, binding.XML) } //BindQuery is a shortcut for c.MustBindWith(obj, binding.Query). func (c *Context) BindQuery(obj interface{}) error { return c.MustBindWith(obj, binding.Query) } //BindYAML is a shortcut for c.MustBindWith(obj, binding.YAML). func (c *Context) BindYAML(obj interface{}) error { return c.MustBindWith(obj, binding.YAML) } //BindHeader is a shortcut for c.MustBindWith(obj, binding.Header). func (c *Context) BindHeader(obj interface{}) error { return c.MustBindWith(obj, binding.Header) } //BindUri binds the passed struct pointer using binding.Uri. //It will abort the request with HTTP 400 if any error occurs. func (c *Context) BindUri(obj interface{}) error { if err := c.ShouldBindUri(obj); err != nil { c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil } //MustBindWith binds the passed struct pointer using the specified binding engine. //It will abort the request with HTTP 400 if any error occurs. //See the binding package. func (c *Context) MustBindWith(obj interface{}, b binding.Binding) error { if err := c.ShouldBindWith(obj, b); err != nil { c.AbortWithError(http.StatusBadRequest, err).SetType(ErrorTypeBind) //nolint: errcheck return err } return nil } //ShouldBind checks the Content-Type to select a binding engine automatically, //Depending the "Content-Type" header different bindings are used: // "application/json" --> JSON binding // "application/xml" --> XML binding //otherwise --> returns an error //It parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. //It decodes the json payload into the struct specified as a pointer. //Like c.Bind() but this method does not set the response status code to 400 and abort if the json is not valid. func (c *Context) ShouldBind(obj interface{}) error { b := binding.Default(c.Request.Method, c.ContentType()) return c.ShouldBindWith(obj, b) } //ShouldBindJSON is a shortcut for c.ShouldBindWith(obj, binding.JSON). func (c *Context) ShouldBindJSON(obj interface{}) error { return c.ShouldBindWith(obj, binding.JSON) } //ShouldBindXML is a shortcut for c.ShouldBindWith(obj, binding.XML). func (c *Context) ShouldBindXML(obj interface{}) error { return c.ShouldBindWith(obj, binding.XML) } //ShouldBindQuery is a shortcut for c.ShouldBindWith(obj, binding.Query). func (c *Context) ShouldBindQuery(obj interface{}) error { return c.ShouldBindWith(obj, binding.Query) } //ShouldBindYAML is a shortcut for c.ShouldBindWith(obj, binding.YAML). func (c *Context) ShouldBindYAML(obj interface{}) error { return c.ShouldBindWith(obj, binding.YAML) } //ShouldBindHeader is a shortcut for c.ShouldBindWith(obj, binding.Header). func (c *Context) ShouldBindHeader(obj interface{}) error { return c.ShouldBindWith(obj, binding.Header) } //ShouldBindUri binds the passed struct pointer using the specified binding engine. func (c *Context) ShouldBindUri(obj interface{}) error { m := make(map[string][]string) for _, v := range c.Params { m[v.Key] = []string{v.Value} } return binding.Uri.BindUri(m, obj) } //ShouldBindWith binds the passed struct pointer using the specified binding engine. //See the binding package. func (c *Context) ShouldBindWith(obj interface{}, b binding.Binding) error { return b.Bind(c.Request, obj) } //ShouldBindBodyWith is similar with ShouldBindWith, but it stores the request //body into the context, and reuse when it is called again. // //NOTE: This method reads the body before binding. So you should use //ShouldBindWith for better performance if you need to call only once. func (c *Context) ShouldBindBodyWith(obj interface{}, bb binding.BindingBody) (err error) { var body []byte if cb, ok := c.Get(BodyBytesKey); ok { if cbb, ok := cb.([]byte); ok { body = cbb } } if body == nil { body, err = ioutil.ReadAll(c.Request.Body) if err != nil { return err } c.Set(BodyBytesKey, body) } return bb.BindBody(body, obj) } //ClientIP implements a best effort algorithm to return the real client IP. //It called c.RemoteIP() under the hood, to check if the remote IP is a trusted proxy or not. //If it's it will then try to parse the headers defined in Engine.RemoteIPHeaders (defaulting to [X-Forwarded-For, X-Real-Ip]). //If the headers are nots syntactically valid OR the remote IP does not correspong to a trusted proxy, //the remote IP (coming form Request.RemoteAddr) is returned. func (c *Context) ClientIP() string { if c.engine.AppEngine { if addr := c.requestHeader("X-Appengine-Remote-Addr"); addr != "" { return addr } } remoteIP, trusted := c.RemoteIP() if remoteIP == nil { return "" } if trusted && c.engine.ForwardedByClientIP && c.engine.RemoteIPHeaders != nil { for _, headerName := range c.engine.RemoteIPHeaders { ip, valid := validateHeader(c.requestHeader(headerName)) if valid { return ip } } } return remoteIP.String() } //RemoteIP parses the IP from Request.RemoteAddr, normalizes and returns the IP (without the port). //It also checks if the remoteIP is a trusted proxy or not. //In order to perform this validation, it will see if the IP is contained within at least one of the CIDR blocks //defined in Engine.TrustedProxies func (c *Context) RemoteIP() (net.IP, bool) { ip, _, err := net.SplitHostPort(strings.TrimSpace(c.Request.RemoteAddr)) if err != nil { return nil, false } remoteIP := net.ParseIP(ip) if remoteIP == nil { return nil, false } if c.engine.trustedCIDRs != nil { for _, cidr := range c.engine.trustedCIDRs { if cidr.Contains(remoteIP) { return remoteIP, true } } } return remoteIP, false } func validateHeader(header string) (clientIP string, valid bool) { if header == "" { return "", false } items := strings.Split(header, ",") for i, ipStr := range items { ipStr = strings.TrimSpace(ipStr) ip := net.ParseIP(ipStr) if ip == nil { return "", false } //We need to return the first IP in the list, but, //we should not early return since we need to validate that //the rest of the header is syntactically valid if i == 0 { clientIP = ipStr valid = true } } return } //ContentType returns the Content-Type header of the request. func (c *Context) ContentType() string { return filterFlags(c.requestHeader("Content-Type")) } //IsWebsocket returns true if the request headers indicate that a websocket //handshake is being initiated by the client. func (c *Context) IsWebsocket() bool { if strings.Contains(strings.ToLower(c.requestHeader("Connection")), "upgrade") && strings.EqualFold(c.requestHeader("Upgrade"), "websocket") { return true } return false } func (c *Context) requestHeader(key string) string { return c.Request.Header.Get(key) }

gin cookie header body c.JSON c.HTML RESPONSE RENDERING

/************************************/ /******** RESPONSE RENDERING ********/ /************************************/ //bodyAllowedForStatus is a copy of http.bodyAllowedForStatus non-exported function. func bodyAllowedForStatus(status int) bool { switch { case status >= 100 && status <= 199: return false case status == http.StatusNoContent: return false case status == http.StatusNotModified: return false } return true } //Status sets the HTTP response code. func (c *Context) Status(code int) { c.Writer.WriteHeader(code) } //Header is a intelligent shortcut for c.Writer.Header().Set(key, value). //It writes a header in the response. //If value == "", this method removes the header `c.Writer.Header().Del(key)` func (c *Context) Header(key, value string) { if value == "" { c.Writer.Header().Del(key) return } c.Writer.Header().Set(key, value) } //GetHeader returns value from request headers. func (c *Context) GetHeader(key string) string { return c.requestHeader(key) } //GetRawData return stream data. func (c *Context) GetRawData() ([]byte, error) { return ioutil.ReadAll(c.Request.Body) } //SetSameSite with cookie func (c *Context) SetSameSite(samesite http.SameSite) { c.sameSite = samesite } //SetCookie adds a Set-Cookie header to the ResponseWriter's headers. //The provided cookie must have a valid Name. Invalid cookies may be //silently dropped. func (c *Context) SetCookie(name, value string, maxAge int, path, domain string, secure, httpOnly bool) { if path == "" { path = "/" } http.SetCookie(c.Writer, &http.Cookie{ Name: name, Value: url.QueryEscape(value), MaxAge: maxAge, Path: path, Domain: domain, SameSite: c.sameSite, Secure: secure, HttpOnly: httpOnly, }) } //Cookie returns the named cookie provided in the request or //ErrNoCookie if not found. And return the named cookie is unescaped. //If multiple cookies match the given name, only one cookie will //be returned. func (c *Context) Cookie(name string) (string, error) { cookie, err := c.Request.Cookie(name) if err != nil { return "", err } val, _ := url.QueryUnescape(cookie.Value) return val, nil } //Render writes the response headers and calls render.Render to render data. func (c *Context) Render(code int, r render.Render) { c.Status(code) if !bodyAllowedForStatus(code) { r.WriteContentType(c.Writer) c.Writer.WriteHeaderNow() return } if err := r.Render(c.Writer); err != nil { panic(err) } } //HTML renders the HTTP template specified by its file name. //It also updates the HTTP code and sets the Content-Type as "text/html". //See http://golang.org/doc/articles/wiki/ func (c *Context) HTML(code int, name string, obj interface{}) { instance := c.engine.HTMLRender.Instance(name, obj) c.Render(code, instance) } //IndentedJSON serializes the given struct as pretty JSON (indented + endlines) into the response body. //It also sets the Content-Type as "application/json". //WARNING: we recommend to use this only for development purposes since printing pretty JSON is //more CPU and bandwidth consuming. Use Context.JSON() instead. func (c *Context) IndentedJSON(code int, obj interface{}) { c.Render(code, render.IndentedJSON{Data: obj}) } //SecureJSON serializes the given struct as Secure JSON into the response body. //Default prepends "while(1)," to response body if the given struct is array values. //It also sets the Content-Type as "application/json". func (c *Context) SecureJSON(code int, obj interface{}) { c.Render(code, render.SecureJSON{Prefix: c.engine.secureJSONPrefix, Data: obj}) } //JSONP serializes the given struct as JSON into the response body. //It adds padding to response body to request data from a server residing in a different domain than the client. //It also sets the Content-Type as "application/javascript". func (c *Context) JSONP(code int, obj interface{}) { callback := c.DefaultQuery("callback", "") if callback == "" { c.Render(code, render.JSON{Data: obj}) return } c.Render(code, render.JsonpJSON{Callback: callback, Data: obj}) } //JSON serializes the given struct as JSON into the response body. //It also sets the Content-Type as "application/json". func (c *Context) JSON(code int, obj interface{}) { c.Render(code, render.JSON{Data: obj}) } //AsciiJSON serializes the given struct as JSON into the response body with unicode to ASCII string. //It also sets the Content-Type as "application/json". func (c *Context) AsciiJSON(code int, obj interface{}) { c.Render(code, render.AsciiJSON{Data: obj}) } //PureJSON serializes the given struct as JSON into the response body. //PureJSON, unlike JSON, does not replace special html characters with their unicode entities. func (c *Context) PureJSON(code int, obj interface{}) { c.Render(code, render.PureJSON{Data: obj}) } //XML serializes the given struct as XML into the response body. //It also sets the Content-Type as "application/xml". func (c *Context) XML(code int, obj interface{}) { c.Render(code, render.XML{Data: obj}) } //YAML serializes the given struct as YAML into the response body. func (c *Context) YAML(code int, obj interface{}) { c.Render(code, render.YAML{Data: obj}) } //ProtoBuf serializes the given struct as ProtoBuf into the response body. func (c *Context) ProtoBuf(code int, obj interface{}) { c.Render(code, render.ProtoBuf{Data: obj}) } //String writes the given string into the response body. func (c *Context) String(code int, format string, values ...interface{}) { c.Render(code, render.String{Format: format, Data: values}) } //Redirect returns a HTTP redirect to the specific location. func (c *Context) Redirect(code int, location string) { c.Render(-1, render.Redirect{ Code: code, Location: location, Request: c.Request, }) } //Data writes some data into the body stream and updates the HTTP code. func (c *Context) Data(code int, contentType string, data []byte) { c.Render(code, render.Data{ ContentType: contentType, Data: data, }) } //DataFromReader writes the specified reader into the body stream and updates the HTTP code. func (c *Context) DataFromReader(code int, contentLength int64, contentType string, reader io.Reader, extraHeaders map[string]string) { c.Render(code, render.Reader{ Headers: extraHeaders, ContentType: contentType, ContentLength: contentLength, Reader: reader, }) } //File writes the specified file into the body stream in an efficient way. func (c *Context) File(filepath string) { http.ServeFile(c.Writer, c.Request, filepath) } //FileFromFS writes the specified file from http.FileSystem into the body stream in an efficient way. func (c *Context) FileFromFS(filepath string, fs http.FileSystem) { defer func(old string) { c.Request.URL.Path = old }(c.Request.URL.Path) c.Request.URL.Path = filepath http.FileServer(fs).ServeHTTP(c.Writer, c.Request) } //FileAttachment writes the specified file into the body stream in an efficient way //On the client side, the file will typically be downloaded with the given filename func (c *Context) FileAttachment(filepath, filename string) { c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename="%s"", filename)) http.ServeFile(c.Writer, c.Request, filepath) } //SSEvent writes a Server-Sent Event into the body stream. func (c *Context) SSEvent(name string, message interface{}) { c.Render(-1, sse.Event{ Event: name, Data: message, }) } //Stream sends a streaming response and returns a boolean //indicates "Is client disconnected in middle of stream" func (c *Context) Stream(step func(w io.Writer) bool) bool { w := c.Writer clientGone := w.CloseNotify() for { select { case <-clientGone: return true default: keepOpen := step(w) w.Flush() if !keepOpen { return false } } } }

Http Request

golang http

package main import ( "fmt" "log" "net/http" ) func main() { http.HandleFunc("/user", func(writer http.ResponseWriter, request *http.Request) { //writer.Write() _, err :=writer.Write([]byte("hello world")) if err!= nil { fmt.Println(err) } }) err := http.ListenAndServe(":8000",nil) if err != nil { log.Fatal("ListenAndServe: ", err) } }

gin handler http.ResponseWriter http.Request gin http.ResponseWriter *ResponseWriter http.Request context

gin koa handler main handler middleware handler middleware handle request next next

package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func m1() gin.HandlerFunc{ return func(c *gin.Context) { fmt.Println("m1 start") c.Next() fmt.Println("m1 end") } } func m2() gin.HandlerFunc { return func(c *gin.Context) { fmt.Println("m2 start") c.Next() fmt.Println("m2 end") } } func main() { r := gin.New() r.Use(m1()) r.Use(m2()) r.GET("/user", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 200, "data": "user", }) }) r.Run(":8000") }

m1 start m2 start m2 end m1 start

gin.Default Logger Recovery panic engine Use

koa m1 user m2 /article

package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func m1() gin.HandlerFunc{ return func(c *gin.Context) { fmt.Println("m1 start") c.Next() fmt.Println("m1 end") } } func m2() gin.HandlerFunc { return func(c *gin.Context) { fmt.Println("m2 start") c.Next() fmt.Println("m2 end") } } func main() { r := gin.New() userRouter := r.Group("/user", m1()) userRouter.GET("/info", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 200, "data": "info", }) }) r.GET("/article", m2(), func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "code": 200, "data": "article", }) }) r.Run(":8000") }

handler *gin.Context handler handler

package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" ) func middle1() gin.HandlerFunc{ return func(c *gin.Context) { fmt.Println("middleware 1") c.Next() } } func middle2(c *gin.Context) { fmt.Println("middleware 2") c.Next() } func main() { r := gin.Default() r.Use(middle1()) r.Use(middle2) r.GET("/user", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "data": "user", }) }) r.Run(":8000") }

Next Abort AbortWithStatus AbortWithStatusJSON AbortWithError header token

package main import ( "github.com/gin-gonic/gin" "net/http" ) func auth() gin.HandlerFunc{ return func(c *gin.Context) { token := c.GetHeader("token") if token == "" { c.AbortWithStatusJSON(401, gin.H{ "message": " ", }) } c.Next() } } func main() { r := gin.Default() r.Use(auth()) r.GET("/user", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "data": "user", }) }) r.Run(":8000") }

gin github.com/gin-gonic/c

www.kancloud.cn/liuqing_wil

www.haohongfan.com/post/2019-0

mp.weixin.qq.com/s/oi8TudWVK

github.com/FlowerWrong

The Top 85 Gin Open Source Projects