网站优化查询代码,搬瓦工做网站,天河网站建设优化,域名会影响网站排名吗文章目录 1、下载并安装 gin#xff1a;2、快速例子3、示例3.1、AsciiJSON3.2、HTML 渲染3.3、JSONP3.4、Multipart/Urlencoded 绑定3.5、Multipart/Urlencoded 表单3.6、PureJSON3.7、Query 和 post form3.8、SecureJSON3.9、XML/JSON/YAML/ProtoBuf 渲染3.10、上传文件3.10.… 文章目录 1、下载并安装 gin2、快速例子3、示例3.1、AsciiJSON3.2、HTML 渲染3.3、JSONP3.4、Multipart/Urlencoded 绑定3.5、Multipart/Urlencoded 表单3.6、PureJSON3.7、Query 和 post form3.8、SecureJSON3.9、XML/JSON/YAML/ProtoBuf 渲染3.10、上传文件3.10.1、单文件3.10.2、多文件 3.11、不使用默认的中间件3.12、从 reader 读取数据3.13、使用 BasicAuth 中间件3.14、使用 HTTP 方法3.15、只绑定 url 查询字符串3.16、在中间件中使用 Goroutine3.17、如何记录日志3.18、将 request body 绑定到不同的结构体中3.19、映射查询字符串或表单参数3.20、查询字符串参数3.21、模型绑定和验证3.22、绑定 HTML 复选框3.23、绑定 Uri3.24、绑定查询字符串或表单数据3.25、绑定表单数据至自定义结构体3.26、自定义 HTTP 配置3.27、自定义中间件3.28、自定义日志文件3.29、自定义验证器3.30、设置和获取 Cookie3.31、路由参数3.32、路由组3.33、运行多个服务3.34、HTTP重定向3.35、静态文件服务3.36、静态资源嵌入 本文是来自官方Gin例子
https://gin-gonic.com/zh-cn/docs/examples/学习Gin的时候把官方例子练习了一次和记录下来。 1、下载并安装 gin
# 1、下载安装依赖包
$ go get -u github.com/gin-gonic/gin# 2、将gin引入到代码中
import github.com/gin-gonic/gin# 3、可选如果使用诸如 http.StatusOK 之类的常量则需要引入 net/http 包
import net/http2、快速例子
package mainimport (github.com/gin-gonic/gin
)func main() {r : gin.Default()r.GET(/ping, func(c *gin.Context) {c.JSON(200, gin.H{message: pong,})})r.Run() // 监听并在 0.0.0.0:8080 上启动服务
}3、示例
3.1、AsciiJSON
使用 AsciiJSON 生成具有转义的非 ASCII 字符的 ASCII-only JSON。
func main() {r : gin.Default()r.GET(/someJSON, func(c *gin.Context) {data : map[string]interface{}{lang: GO语言,tag: br,}// 输出 : {lang:GO\u8bed\u8a00,tag:\u003cbr\u003e}c.AsciiJSON(http.StatusOK, data)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.2、HTML 渲染
使用 LoadHTMLGlob() 或者 LoadHTMLFiles()
func main() {router : gin.Default()router.LoadHTMLGlob(templates/*)//router.LoadHTMLFiles(templates/template1.html, templates/template2.html)router.GET(/index, func(c *gin.Context) {c.HTML(http.StatusOK, index.tmpl, gin.H{title: Main website,})})router.Run(:8080)
}templates/index.tmpl
htmlh1{{ .title }}/h1
/html使用不同目录下名称相同的模板
func main() {router : gin.Default()router.LoadHTMLGlob(templates/**/*)router.GET(/posts/index, func(c *gin.Context) {c.HTML(http.StatusOK, posts/index.tmpl, gin.H{title: Posts,})})router.GET(/users/index, func(c *gin.Context) {c.HTML(http.StatusOK, users/index.tmpl, gin.H{title: Users,})})router.Run(:8080)
}templates/posts/index.tmpl
{{ define posts/index.tmpl }}
htmlh1{{ .title }}
/h1
pUsing posts/index.tmpl/p
/html
{{ end }}templates/users/index.tmpl
{{ define users/index.tmpl }}
htmlh1{{ .title }}
/h1
pUsing users/index.tmpl/p
/html
{{ end }}自定义模板渲染器 你可以使用自定义的 html 模板渲染
import html/templatefunc main() {router : gin.Default()html : template.Must(template.ParseFiles(file1, file2))router.SetHTMLTemplate(html)router.Run(:8080)
}自定义分隔符 你可以使用自定义分隔 r : gin.Default()r.Delims({[{, }]})r.LoadHTMLGlob(/path/to/templates)自定义模板功能 查看详细示例代码。
main.go
import (fmthtml/templatenet/httptimegithub.com/gin-gonic/gin
)func formatAsDate(t time.Time) string {year, month, day : t.Date()return fmt.Sprintf(%d/%02d/%02d, year, month, day)
}func main() {router : gin.Default()router.Delims({[{, }]})router.SetFuncMap(template.FuncMap{formatAsDate: formatAsDate,})router.LoadHTMLFiles(./testdata/template/raw.tmpl)router.GET(/raw, func(c *gin.Context) {c.HTML(http.StatusOK, raw.tmpl, map[string]interface{}{now: time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC),})})router.Run(:8080)
}raw.tmpl
Date: {[{.now | formatAsDate}]}结果
Date: 2017/07/013.3、JSONP
使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调则将回调添加到响应体中。
func main() {
r : gin.Default()r.GET(/JSONP, func(c *gin.Context) {data : map[string]interface{}{foo: bar,}// /JSONP?callbackx// 将输出x({\foo\:\bar\})c.JSONP(http.StatusOK, data)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.4、Multipart/Urlencoded 绑定
package mainimport (github.com/gin-gonic/gin
)type LoginForm struct {User string form:user binding:requiredPassword string form:password binding:required
}func main() {router : gin.Default()router.POST(/login, func(c *gin.Context) {// 你可以使用显式绑定声明绑定 multipart form// c.ShouldBindWith(form, binding.Form)// 或者简单地使用 ShouldBind 方法自动绑定var form LoginForm// 在这种情况下将自动选择合适的绑定if c.ShouldBind(form) nil {if form.User user form.Password password {c.JSON(200, gin.H{status: you are logged in})} else {c.JSON(401, gin.H{status: unauthorized})}}})router.Run(:8080)
}测试
$ curl -v --form useruser --form passwordpassword http://localhost:8080/login3.5、Multipart/Urlencoded 表单
func main() {router : gin.Default()router.POST(/form_post, func(c *gin.Context) {message : c.PostForm(message)nick : c.DefaultPostForm(nick, anonymous)c.JSON(200, gin.H{status: posted,message: message,nick: nick,})})router.Run(:8080)
}3.6、PureJSON
通常JSON 使用 unicode 替换特殊 HTML 字符例如 变为 \ u003c。如果要按字面对这些字符进行编码则可以使用 PureJSON。Go 1.6 及更低版本无法使用此功能。
func main() {
r : gin.Default()// 提供 unicode 实体r.GET(/json, func(c *gin.Context) {c.JSON(200, gin.H{html: bHello, world!/b,})})// 提供字面字符r.GET(/purejson, func(c *gin.Context) {c.PureJSON(200, gin.H{html: bHello, world!/b,})})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.7、Query 和 post form
POST /post?id1234page1 HTTP/1.1
Content-Type: application/x-www-form-urlencodednamemanumessagethis_is_great
func main() {
router : gin.Default()router.POST(/post, func(c *gin.Context) {id : c.Query(id)page : c.DefaultQuery(page, 0)name : c.PostForm(name)message : c.PostForm(message)fmt.Printf(id: %s; page: %s; name: %s; message: %s, id, page, name, message)})router.Run(:8080)
}id: 1234; page: 1; name: manu; message: this_is_great3.8、SecureJSON
使用 SecureJSON 防止 json 劫持。如果给定的结构是数组值则默认预置 “while(1),” 到响应体。
func main() {
r : gin.Default()// 你也可以使用自己的 SecureJSON 前缀// r.SecureJsonPrefix()]},\n)r.GET(/someJSON, func(c *gin.Context) {names : []string{lena, austin, foo}// 将输出while(1);[lena,austin,foo]c.SecureJSON(http.StatusOK, names)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.9、XML/JSON/YAML/ProtoBuf 渲染
func main() {
r : gin.Default()// gin.H 是 map[string]interface{} 的一种快捷方式r.GET(/someJSON, func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{message: hey, status: http.StatusOK})})r.GET(/moreJSON, func(c *gin.Context) {// 你也可以使用一个结构体var msg struct {Name string json:userMessage stringNumber int}msg.Name Lenamsg.Message heymsg.Number 123// 注意 msg.Name 在 JSON 中变成了 user// 将输出{user: Lena, Message: hey, Number: 123}c.JSON(http.StatusOK, msg)})r.GET(/someXML, func(c *gin.Context) {c.XML(http.StatusOK, gin.H{message: hey, status: http.StatusOK})})r.GET(/someYAML, func(c *gin.Context) {c.YAML(http.StatusOK, gin.H{message: hey, status: http.StatusOK})})r.GET(/someProtoBuf, func(c *gin.Context) {reps : []int64{int64(1), int64(2)}label : test// protobuf 的具体定义写在 testdata/protoexample 文件中。data : protoexample.Test{Label: label,Reps: reps,}// 请注意数据在响应中变为二进制数据// 将输出被 protoexample.Test protobuf 序列化了的数据c.ProtoBuf(http.StatusOK, data)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.10、上传文件
3.10.1、单文件
func main() {
router : gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory 8 20 // 8 MiB
router.POST(/upload, func(c *gin.Context) {
// 单文件
file, _ : c.FormFile(file)
log.Println(file.Filename)dst : ./ file.Filename// 上传文件至指定的完整文件路径c.SaveUploadedFile(file, dst)c.String(http.StatusOK, fmt.Sprintf(%s uploaded!, file.Filename))})router.Run(:8080)
}如何使用 curl
curl -X POST http://localhost:8080/upload \
-F file/Users/appleboy/test.zip \
-H Content-Type: multipart/form-data3.10.2、多文件
func main() {
router : gin.Default()
// 为 multipart forms 设置较低的内存限制 (默认是 32 MiB)
router.MaxMultipartMemory 8 20 // 8 MiB
router.POST(/upload, func(c *gin.Context) {
// Multipart form
form, _ : c.MultipartForm()
files : form.File[upload[]]for _, file : range files {log.Println(file.Filename)// 上传文件至指定目录c.SaveUploadedFile(file, dst)}c.String(http.StatusOK, fmt.Sprintf(%d files uploaded!, len(files)))})router.Run(:8080)
}如何使用 curl:
curl -X POST http://localhost:8080/upload \
-F upload[]/Users/appleboy/test1.zip \
-F upload[]/Users/appleboy/test2.zip \
-H Content-Type: multipart/form-data3.11、不使用默认的中间件
使用
r : gin.New()代替
// Default 使用 Logger 和 Recovery 中间件
r : gin.Default()3.12、从 reader 读取数据
func main() {
router : gin.Default()
router.GET(/someDataFromReader, func(c *gin.Context) {
response, err : http.Get(https://raw.githubusercontent.com/gin-gonic/logo/master/color.png)
if err ! nil || response.StatusCode ! http.StatusOK {
c.Status(http.StatusServiceUnavailable)
return
}reader : response.BodycontentLength : response.ContentLengthcontentType : response.Header.Get(Content-Type)extraHeaders : map[string]string{Content-Disposition: attachment; filenamegopher.png,}c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders)})router.Run(:8080)
}3.13、使用 BasicAuth 中间件
// 模拟一些私人数据
var secrets gin.H{foo: gin.H{email: foobar.com, phone: 123433},austin: gin.H{email: austinexample.com, phone: 666},lena: gin.H{email: lenaguapa.com, phone: 523443},
}func main() {
r : gin.Default()// 路由组使用 gin.BasicAuth() 中间件// gin.Accounts 是 map[string]string 的一种快捷方式authorized : r.Group(/admin, gin.BasicAuth(gin.Accounts{foo: bar,austin: 1234,lena: hello2,manu: 4321,}))// /admin/secrets 端点// 触发 localhost:8080/admin/secretsauthorized.GET(/secrets, func(c *gin.Context) {// 获取用户它是由 BasicAuth 中间件设置的user : c.MustGet(gin.AuthUserKey).(string)if secret, ok : secrets[user]; ok {c.JSON(http.StatusOK, gin.H{user: user, secret: secret})} else {c.JSON(http.StatusOK, gin.H{user: user, secret: NO SECRET :(})}})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.14、使用 HTTP 方法
func main() {
// 禁用控制台颜色
// gin.DisableConsoleColor()// 使用默认中间件logger 和 recovery 中间件创建 gin 路由router : gin.Default()router.GET(/someGet, getting)router.POST(/somePost, posting)router.PUT(/somePut, putting)router.DELETE(/someDelete, deleting)router.PATCH(/somePatch, patching)router.HEAD(/someHead, head)router.OPTIONS(/someOptions, options)// 默认在 8080 端口启动服务除非定义了一个 PORT 的环境变量。router.Run()// router.Run(:3000) hardcode 端口号
}3.15、只绑定 url 查询字符串
ShouldBindQuery 函数只绑定 url 查询参数而忽略 post 数据。参阅详细信息.
package mainimport (
loggithub.com/gin-gonic/gin
)type Person struct {Name string form:nameAddress string form:address
}func main() {route : gin.Default()route.Any(/testing, startPage)route.Run(:8085)
}func startPage(c *gin.Context) {var person Personif c.ShouldBindQuery(person) nil {log.Println( Only Bind By Query String )log.Println(person.Name)log.Println(person.Address)}c.String(200, Success)
}3.16、在中间件中使用 Goroutine
当在中间件或 handler 中启动新的 Goroutine 时不能使用原始的上下文必须使用只读副本。
func main() {r : gin.Default()r.GET(/long_async, func(c *gin.Context) {// 创建在 goroutine 中使用的副本cCp : c.Copy()go func() {// 用 time.Sleep() 模拟一个长任务。time.Sleep(5 * time.Second)// 请注意您使用的是复制的上下文 cCp这一点很重要log.Println(Done! in path cCp.Request.URL.Path)}()})r.GET(/long_sync, func(c *gin.Context) {// 用 time.Sleep() 模拟一个长任务。time.Sleep(5 * time.Second)// 因为没有使用 goroutine不需要拷贝上下文log.Println(Done! in path c.Request.URL.Path)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.17、如何记录日志
func main() {// 禁用控制台颜色将日志写入文件时不需要控制台颜色。gin.DisableConsoleColor()// 记录到文件。f, _ : os.Create(gin.log)gin.DefaultWriter io.MultiWriter(f)// 如果需要同时将日志写入文件和控制台请使用以下代码。// gin.DefaultWriter io.MultiWriter(f, os.Stdout)router : gin.Default()router.GET(/ping, func(c *gin.Context) {c.String(200, pong)})router.Run(:8080)
}3.18、将 request body 绑定到不同的结构体中
一般通过调用 c.Request.Body 方法绑定数据但不能多次调用这个方法。
type formA struct {Foo string json:foo xml:foo binding:required
}type formB struct {Bar string json:bar xml:bar binding:required
}func SomeHandler(c *gin.Context) {objA : formA{}objB : formB{}// c.ShouldBind 使用了 c.Request.Body不可重用。if errA : c.ShouldBind(objA); errA nil {c.String(http.StatusOK, the body should be formA)// 因为现在 c.Request.Body 是 EOF所以这里会报错。} else if errB : c.ShouldBind(objB); errB nil {c.String(http.StatusOK, the body should be formB)} else {...}
}要想多次绑定可以使用 c.ShouldBindBodyWith.
func SomeHandler(c *gin.Context) {objA : formA{}objB : formB{}// 读取 c.Request.Body 并将结果存入上下文。if errA : c.ShouldBindBodyWith(objA, binding.JSON); errA nil {c.String(http.StatusOK, the body should be formA)// 这时, 复用存储在上下文中的 body。} else if errB : c.ShouldBindBodyWith(objB, binding.JSON); errB nil {c.String(http.StatusOK, the body should be formB JSON)// 可以接受其他格式} else if errB2 : c.ShouldBindBodyWith(objB, binding.XML); errB2 nil {c.String(http.StatusOK, the body should be formB XML)} else {...}
}c.ShouldBindBodyWith 会在绑定之前将 body 存储到上下文中。 这会对性能造成轻微影响如果调用一次就能完成绑定的话那就不要用这个方法。 只有某些格式需要此功能如 JSON, XML, MsgPack, ProtoBuf。 对于其他格式, 如 Query, Form, FormPost, FormMultipart 可以多次调用 c.ShouldBind() 而不会造成任任何性能损失 (详见 #1341)。
3.19、映射查询字符串或表单参数
POST /post?ids[a]1234ids[b]hello HTTP/1.1
Content-Type: application/x-www-form-urlencodednames[first]thinkerounames[second]tianoufunc main() {router : gin.Default()router.POST(/post, func(c *gin.Context) {ids : c.QueryMap(ids)names : c.PostFormMap(names)fmt.Printf(ids: %v; names: %v, ids, names)})router.Run(:8080)
}ids: map[b:hello a:1234], names: map[second:tianou first:thinkerou]3.20、查询字符串参数
func main() {
router : gin.Default()// 使用现有的基础请求对象解析查询字符串参数。// 示例 URL /welcome?firstnameJanelastnameDoerouter.GET(/welcome, func(c *gin.Context) {firstname : c.DefaultQuery(firstname, Guest)lastname : c.Query(lastname) // c.Request.URL.Query().Get(lastname) 的一种快捷方式c.String(http.StatusOK, Hello %s %s, firstname, lastname)})router.Run(:8080)
}3.21、模型绑定和验证
要将请求体绑定到结构体中使用模型绑定。 Gin目前支持JSON、XML、YAML和标准表单值的绑定foobarboobaz。
Gin使用 go-playground/validator/v10 进行验证。 查看标签用法的全部文档.
使用时需要在要绑定的所有字段上设置相应的tag。 例如使用 JSON 绑定时设置字段标签为 json:“fieldname”。
Gin提供了两类绑定方法
Type - Must bind Methods - Bind, BindJSON, BindXML, BindQuery, BindYAMLBehavior - 这些方法属于 MustBindWith 的具体调用。 如果发生绑定错误则请求终止并触发 c.AbortWithError(400, err).SetType(ErrorTypeBind)。响应状态码被设置为 400 并且 Content-Type 被设置为 text/plain; charsetutf-8。 如果您在此之后尝试设置响应状态码Gin会输出日志 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422。 如果您希望更好地控制绑定考虑使用 ShouldBind 等效方法。 Type - Should bind Methods - ShouldBind, ShouldBindJSON, ShouldBindXML, ShouldBindQuery, ShouldBindYAMLBehavior - 这些方法属于 ShouldBindWith 的具体调用。 如果发生绑定错误Gin 会返回错误并由开发者处理错误和请求。 使用 Bind 方法时Gin 会尝试根据 Content-Type 推断如何绑定。 如果你明确知道要绑定什么可以使用 MustBindWith 或 ShouldBindWith。
你也可以指定必须绑定的字段。 如果一个字段的 tag 加上了 binding:“required”但绑定时是空值, Gin 会报错。
// 绑定 JSON
type Login struct {
User string form:user json:user xml:user binding:required
Password string form:password json:password xml:password binding:required
}func main() {
router : gin.Default()// 绑定 JSON ({user: manu, password: 123})router.POST(/loginJSON, func(c *gin.Context) {var json Loginif err : c.ShouldBindJSON(json); err ! nil {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})return}if json.User ! manu || json.Password ! 123 {c.JSON(http.StatusUnauthorized, gin.H{status: unauthorized})return} c.JSON(http.StatusOK, gin.H{status: you are logged in})})// 绑定 XML (// ?xml version1.0 encodingUTF-8?// root// usermanu/user// password123/password// /root)router.POST(/loginXML, func(c *gin.Context) {var xml Loginif err : c.ShouldBindXML(xml); err ! nil {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})return}if xml.User ! manu || xml.Password ! 123 {c.JSON(http.StatusUnauthorized, gin.H{status: unauthorized})return} c.JSON(http.StatusOK, gin.H{status: you are logged in})})// 绑定 HTML 表单 (usermanupassword123)router.POST(/loginForm, func(c *gin.Context) {var form Login// 根据 Content-Type Header 推断使用哪个绑定器。if err : c.ShouldBind(form); err ! nil {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})return}if form.User ! manu || form.Password ! 123 {c.JSON(http.StatusUnauthorized, gin.H{status: unauthorized})return} c.JSON(http.StatusOK, gin.H{status: you are logged in})})// 监听并在 0.0.0.0:8080 上启动服务router.Run(:8080)
}3.22、绑定 HTML 复选框
main.go
...type myForm struct {
Colors []string form:colors[]
}...func formHandler(c *gin.Context) {
var fakeForm myForm
c.ShouldBind(fakeForm)
c.JSON(200, gin.H{color: fakeForm.Colors})
}...form.html
form action/ methodPOSTpCheck some colors/plabel forredRed/labelinput typecheckbox namecolors[] valuered idredlabel forgreenGreen/labelinput typecheckbox namecolors[] valuegreen idgreenlabel forblueBlue/labelinput typecheckbox namecolors[] valueblue idblueinput typesubmit
/form结果:
{color:[red,green,blue]}3.23、绑定 Uri
package mainimport github.com/gin-gonic/gintype Person struct {ID string uri:id binding:required,uuidName string uri:name binding:required
}func main() {route : gin.Default()route.GET(/:name/:id, func(c *gin.Context) {var person Personif err : c.ShouldBindUri(person); err ! nil {c.JSON(400, gin.H{msg: err.Error()})return}c.JSON(200, gin.H{name: person.Name, uuid: person.ID})})route.Run(:8088)
}测试:
$ curl -v localhost:8088/thinkerou/987fbc97-4bed-5078-9f07-9141ba07c9f3
$ curl -v localhost:8088/thinkerou/not-uuid3.24、绑定查询字符串或表单数据
package mainimport (logtimegithub.com/gin-gonic/gin
)type Person struct {Name string form:nameAddress string form:addressBirthday time.Time form:birthday time_format:2006-01-02 time_utc:1
}func main() {route : gin.Default()route.GET(/testing, startPage)route.Run(:8085)
}func startPage(c *gin.Context) {var person Person// 如果是 GET 请求只使用 Form 绑定引擎query。// 如果是 POST 请求首先检查 content-type 是否为 JSON 或 XML然后再使用 Formform-data。// 查看更多https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88if c.ShouldBind(person) nil {log.Println(person.Name)log.Println(person.Address)log.Println(person.Birthday)}c.String(200, Success)
}测试
$ curl -X GET localhost:8085/testing?nameappleboyaddressxyzbirthday1992-03-153.25、绑定表单数据至自定义结构体
以下示例使用自定义结构体
type StructA struct {FieldA string form:field_a
}type StructB struct {NestedStruct StructAFieldB string form:field_b
}type StructC struct {NestedStructPointer *StructAFieldC string form:field_c
}type StructD struct {NestedAnonyStruct struct {FieldX string form:field_x}FieldD string form:field_d
}func GetDataB(c *gin.Context) {var b StructBc.Bind(b)c.JSON(200, gin.H{a: b.NestedStruct,b: b.FieldB,})
}func GetDataC(c *gin.Context) {var b StructCc.Bind(b)c.JSON(200, gin.H{a: b.NestedStructPointer,c: b.FieldC,})
}func GetDataD(c *gin.Context) {var b StructDc.Bind(b)c.JSON(200, gin.H{x: b.NestedAnonyStruct,d: b.FieldD,})
}func main() {r : gin.Default()r.GET(/getb, GetDataB)r.GET(/getc, GetDataC)r.GET(/getd, GetDataD)r.Run()
}使用 curl 命令结果
$ curl http://localhost:8080/getb?field_ahellofield_bworld
{a:{FieldA:hello},b:world}
$ curl http://localhost:8080/getc?field_ahellofield_cworld
{a:{FieldA:hello},c:world}
$ curl http://localhost:8080/getd?field_xhellofield_dworld
{d:world,x:{FieldX:hello}}3.26、自定义 HTTP 配置
直接使用 http.ListenAndServe()如下所示
func main() {router : gin.Default()http.ListenAndServe(:8080, router)
}或
func main() {router : gin.Default()s : http.Server{Addr: :8080,Handler: router,ReadTimeout: 10 * time.Second,WriteTimeout: 10 * time.Second,MaxHeaderBytes: 1 20,}s.ListenAndServe()
}3.27、自定义中间件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()// 设置 example 变量c.Set(example, 12345)// 请求前c.Next()// 请求后latency : time.Since(t)log.Print(latency)// 获取发送的 statusstatus : c.Writer.Status()log.Println(status)}
}func main() {
r : gin.New()
r.Use(Logger())r.GET(/test, func(c *gin.Context) {example : c.MustGet(example).(string)// 打印12345log.Println(example)})// 监听并在 0.0.0.0:8080 上启动服务r.Run(:8080)
}3.28、自定义日志文件
func main() {router : gin.New()// LoggerWithFormatter 中间件会写入日志到 gin.DefaultWriter// 默认 gin.DefaultWriter os.Stdoutrouter.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {// 你的自定义格式return fmt.Sprintf(%s - [%s] \%s %s %s %d %s \%s\ %s\\n,param.ClientIP,param.TimeStamp.Format(time.RFC1123),param.Method,param.Path,param.Request.Proto,param.StatusCode,param.Latency,param.Request.UserAgent(),param.ErrorMessage,)}))router.Use(gin.Recovery())router.GET(/ping, func(c *gin.Context) {c.String(200, pong)})router.Run(:8080)
}3.29、自定义验证器
注册自定义验证器查看示例代码.
package main
import (net/httpreflecttimegithub.com/gin-gonic/gingithub.com/gin-gonic/gin/bindinggithub.com/go-playground/validator/v10
)
// Booking 包含绑定和验证的数据。
type Booking struct {CheckIn time.Time form:check_in binding:required,bookabledate time_format:2006-01-02CheckOut time.Time form:check_out binding:required,gtfieldCheckIn,bookabledate time_format:2006-01-02
}var bookableDate validator.Func func(fl validator.FieldLevel) bool {date, ok : fl.Field().Interface().(time.Time)if ok {today : time.Now()if today.After(date) {return false}}return true
}func main() {route : gin.Default()if v, ok : binding.Validator.Engine().(*validator.Validate); ok {v.RegisterValidation(bookabledate, bookableDate)}route.GET(/bookable, getBookable)route.Run(:8085)
}func getBookable(c *gin.Context) {var b Bookingif err : c.ShouldBindWith(b, binding.Query); err nil {c.JSON(http.StatusOK, gin.H{message: Booking dates are valid!})} else {c.JSON(http.StatusBadRequest, gin.H{error: err.Error()})}
}3.30、设置和获取 Cookie
import (fmtgithub.com/gin-gonic/gin
)func main() {router : gin.Default()router.GET(/cookie, func(c *gin.Context) {cookie, err : c.Cookie(gin_cookie)if err ! nil {cookie NotSetc.SetCookie(gin_cookie, test, 3600, /, localhost, false, true)}fmt.Printf(Cookie value: %s \n, cookie)})router.Run()
}3.31、路由参数
func main() {
router : gin.Default()// 此 handler 将匹配 /user/john 但不会匹配 /user/ 或者 /userrouter.GET(/user/:name, func(c *gin.Context) {name : c.Param(name)c.String(http.StatusOK, Hello %s, name)})// 此 handler 将匹配 /user/john/ 和 /user/john/send// 如果没有其他路由匹配 /user/john它将重定向到 /user/john/router.GET(/user/:name/*action, func(c *gin.Context) {name : c.Param(name)action : c.Param(action)message : name is actionc.String(http.StatusOK, message)})router.Run(:8080)
}3.32、路由组
func main() {router : gin.Default()// 简单的路由组: v1v1 : router.Group(/v1){v1.POST(/login, loginEndpoint)v1.POST(/submit, submitEndpoint)v1.POST(/read, readEndpoint)}// 简单的路由组: v2v2 : router.Group(/v2){v2.POST(/login, loginEndpoint)v2.POST(/submit, submitEndpoint)v2.POST(/read, readEndpoint)}router.Run(:8080)
}3.33、运行多个服务
package main
import (lognet/httptimegithub.com/gin-gonic/gingolang.org/x/sync/errgroup
)
var (g errgroup.Group
)func router01() http.Handler {e : gin.New()e.Use(gin.Recovery())e.GET(/, func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{code: http.StatusOK,error: Welcome server 01,},)})return e
}func router02() http.Handler {e : gin.New()e.Use(gin.Recovery())e.GET(/, func(c *gin.Context) {c.JSON(http.StatusOK,gin.H{code: http.StatusOK,error: Welcome server 02,},)})return e
}func main() {server01 : http.Server{Addr: :8080,Handler: router01(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}server02 : http.Server{Addr: :8081,Handler: router02(),ReadTimeout: 5 * time.Second,WriteTimeout: 10 * time.Second,}g.Go(func() error {return server01.ListenAndServe()})g.Go(func() error {return server02.ListenAndServe()})if err : g.Wait(); err ! nil {log.Fatal(err)}
}3.34、HTTP重定向
HTTP 重定向很容易。 内部、外部重定向均支持。
r.GET(/test, func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, http://www.google.com/)
})通过 POST 方法进行 HTTP 重定向。请参考 issue#444
r.POST(/test, func(c *gin.Context) {c.Redirect(http.StatusFound, /foo)
})路由重定向使用 HandleContext
r.GET(/test, func(c *gin.Context) {c.Request.URL.Path /test2r.HandleContext(c)
})
r.GET(/test2, func(c *gin.Context) {c.JSON(200, gin.H{hello: world})
})3.35、静态文件服务
func main() {router : gin.Default()router.Static(/assets, ./assets)router.StaticFS(/more_static, http.Dir(my_file_system))router.StaticFile(/favicon.ico, ./resources/favicon.ico)// 监听并在 0.0.0.0:8080 上启动服务router.Run(:8080)
}3.36、静态资源嵌入
你可以使用 go-assets 将静态资源打包到可执行文件中。
go get github.com/jessevdk/go-assets-builderfunc main() {r : gin.New()t, err : loadTemplate()if err ! nil {panic(err)}r.SetHTMLTemplate(t)r.GET(/, func(c *gin.Context) {c.HTML(http.StatusOK, /html/index.tmpl, nil)})r.Run(:8080)
}// loadTemplate 加载由 go-assets-builder 嵌入的模板
func loadTemplate() (*template.Template, error) {t : template.New()for name, file : range Assets.Files {if file.IsDir() || !strings.HasSuffix(name, .tmpl) {continue}h, err : ioutil.ReadAll(file)if err ! nil {return nil, err}t, err t.New(name).Parse(string(h))if err ! nil {return nil, err}}return t, nil
}