First complete translation

This commit is contained in:
Bro Qiang 2018-07-13 21:31:33 +08:00
parent cd1ec10f0c
commit 00b51579f9

View File

@ -24,40 +24,40 @@ Gin 是一个用 Go 语言编写的 WEB 框架。它具有和 maritini 类似的
- [API 示例](#api-示例)
- [使用 GET,POST,PUT,PATCH,DELETE and OPTIONS](#使用-get-post-put-patch-delete-and-options)
- [path 中的参数](#path-中的参数)
- [Querystring parameters](#querystring-parameters)
- [Multipart/Urlencoded Form](#multiparturlencoded-form)
- [Another example: query + post form](#another-example-query--post-form)
- [Upload files](#upload-files)
- [Grouping routes](#grouping-routes)
- [Blank Gin without middleware by default](#blank-gin-without-middleware-by-default)
- [Using middleware](#using-middleware)
- [How to write log file](#how-to-write-log-file)
- [Model binding and validation](#model-binding-and-validation)
- [Custom Validators](#custom-validators)
- [Only Bind Query String](#only-bind-query-string)
- [Bind Query String or Post Data](#bind-query-string-or-post-data)
- [Bind HTML checkboxes](#bind-html-checkboxes)
- [Multipart/Urlencoded binding](#multiparturlencoded-binding)
- [XML, JSON and YAML rendering](#xml-json-and-yaml-rendering)
- [JSONP rendering](#jsonp)
- [Serving static files](#serving-static-files)
- [Serving data from reader](#serving-data-from-reader)
- [HTML rendering](#html-rendering)
- [Multitemplate](#multitemplate)
- [Redirects](#redirects)
- [Custom Middleware](#custom-middleware)
- [Using BasicAuth() middleware](#using-basicauth-middleware)
- [Goroutines inside a middleware](#goroutines-inside-a-middleware)
- [Custom HTTP configuration](#custom-http-configuration)
- [Support Let's Encrypt](#support-lets-encrypt)
- [Run multiple service using Gin](#run-multiple-service-using-gin)
- [Graceful restart or stop](#graceful-restart-or-stop)
- [Build a single binary with templates](#build-a-single-binary-with-templates)
- [Bind form-data request with custom struct](#bind-form-data-request-with-custom-struct)
- [Try to bind body into different structs](#try-to-bind-body-into-different-structs)
- [http2 server push](#http2-server-push)
- [Testing](#testing)
- [Users](#users--)
- [请求参数](#请求参数)
- [Multipart/Urlencoded 表单](#Multipart/Urlencoded-表单)
- [其他示例: 请求参数 + post form](#其他示例:-请求参数--post-form)
- [上传文件](#上传文件)
- [组路由](#组路由)
- [默认的没有中间件的空白 Gin](#默认的没有中间件的空白-Gin)
- [使用中间件](#使用中间件)
- [如何写入日志文件](#如何写入日志文件)
- [模型绑定和验证](#模型绑定和验证)
- [自定义验证器](#自定义验证器)
- [只绑定查询字符串](#只绑定查询字符串)
- [绑定查询字符串或 post 数据](#绑定查询字符串或-post-数据)
- [绑定 HTML 复选框](#绑定-HTML-复选框)
- [Multipart/Urlencoded 绑定](#Multipart/Urlencoded-绑定)
- [XML, JSON 和 YAML 渲染](#XML-JSON-和-YAML-渲染)
- [JSONP 渲染](#jsonp)
- [静态文件服务](#静态文件服务)
- [从 reader 提供数据](#从-reader-提供数据)
- [HTML 渲染](#HTML-渲染)
- [多模板](#多模板)
- [重定向](#重定向)
- [自定义中间件](#自定义中间件)
- [使用 BasicAuth() 中间件](#使用-BasicAuth-中间件)
- [在中间件中使用协成](#在中间件中使用协成)
- [自定义 HTTP 配置](#自定义-HTTP-配置)
- [支持 Let's Encrypt](#支持-Let's-Encrypt)
- [使用 Gin 运行多个服务](#使用-Gin-运行多个服务)
- [正常的重启或停止](#正常的重启或停止)
- [使用模板构建单个二进制文件](#使用模板构建单个二进制文件)
- [使用自定义结构绑定表单数据请求](#使用自定义结构绑定表单数据请求)
- [尝试将 body 绑定到不同的结构中](#尝试将-body-绑定到不同的结构中)
- [HTTP2 服务器推送](#HTTP2-服务器推送)
- [测试](#测试)
- [使用者](#使用者--)
## 安装
@ -273,7 +273,7 @@ func main() {
}
```
### Multipart/Urlencoded Form
### Multipart/Urlencoded 表单
```go
func main() {
@ -583,11 +583,11 @@ $ curl -v -X POST \
**跳过验证**
When running the above example using the above the `curl` command, it returns error. Because the example use `binding:"required"` for `Password`. If use `binding:"-"` for `Password`, then it will not return error when running the above example again.
当在命令行上使用 `curl` 运行上面的示例时,它会返回一个错误。因为示例给 `Password` 绑定了 `binding:"required"` 。如果 `Password` 使用 `binding:"-"` ,然后再次运行上面的示例,它将不会返回错误。
### Custom Validators
### 自定义验证器
It is also possible to register custom validators. See the [example code](examples/custom-validation/server.go).
也可以注册自定义验证器。 参见 [示例代码](examples/custom-validation/server.go) 。
[embedmd]:# (examples/custom-validation/server.go go)
```go
@ -650,12 +650,12 @@ $ curl "localhost:8085/bookable?check_in=2018-03-08&check_out=2018-03-09"
{"error":"Key: 'Booking.CheckIn' Error:Field validation for 'CheckIn' failed on the 'bookabledate' tag"}
```
[Struct level validations](https://github.com/go-playground/validator/releases/tag/v8.7) can also be registed this way.
See the [struct-lvl-validation example](examples/struct-lvl-validations) to learn more.
[结构级别的验证](https://github.com/go-playground/validator/releases/tag/v8.7) 也可以这样注册。
查看 [示例 struct-lvl-validation ](examples/struct-lvl-validations) 学习更多。
### Only Bind Query String
### 只绑定查询字符串
`ShouldBindQuery` function only binds the query params and not the post data. See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).
`ShouldBindQuery` 函数只绑定查询参数并且没有 post 数据。查看 [详细信息](https://github.com/gin-gonic/gin/issues/742#issuecomment-315953017).
```go
package main
@ -689,9 +689,9 @@ func startPage(c *gin.Context) {
```
### Bind Query String or Post Data
### 绑定查询字符串或 post 数据
See the [detail information](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292).
查看 [详细信息](https://github.com/gin-gonic/gin/issues/742#issuecomment-264681292).
```go
package main
@ -714,9 +714,9 @@ func main() {
func startPage(c *gin.Context) {
var person Person
// If `GET`, only `Form` binding engine (`query`) used.
// If `POST`, first checks the `content-type` for `JSON` or `XML`, then uses `Form` (`form-data`).
// See more at https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
// 如果是 `GET`, 只使用 `Form` 绑定引擎 (`query`) 。
// 如果 `POST`, 首先检查 `content-type``JSON``XML`, 然后使用 `Form` (`form-data`) 。
// 在这里查看更多信息 https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L48
if c.ShouldBind(&person) == nil {
log.Println(person.Name)
log.Println(person.Address)
@ -727,14 +727,14 @@ func startPage(c *gin.Context) {
}
```
Test it with:
测试它:
```sh
$ curl -X GET "localhost:8085/testing?name=appleboy&address=xyz&birthday=1992-03-15"
```
### Bind HTML checkboxes
### 绑定 HTML 复选框
See the [detail information](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
查看 [详细信息](https://github.com/gin-gonic/gin/issues/129#issuecomment-124260092)
main.go
@ -778,7 +778,7 @@ result:
{"color":["red","green","blue"]}
```
### Multipart/Urlencoded binding
### Multipart/Urlencoded 绑定
```go
package main
@ -795,11 +795,11 @@ type LoginForm struct {
func main() {
router := gin.Default()
router.POST("/login", func(c *gin.Context) {
// you can bind multipart form with explicit binding declaration:
// c.ShouldBindWith(&form, binding.Form)
// or you can simply use autobinding with ShouldBind method:
// 你可以使用显示绑定声明绑定 multipart 表单:
// c.ShouldBindWith(&form, binding.Form
// 或者你可以使用 ShouldBind 方法去简单的使用自动绑定:
var form LoginForm
// in this case proper binding will be automatically selected
// 在这种情况下,将自动选择适合的绑定
if c.ShouldBind(&form) == nil {
if form.User == "user" && form.Password == "password" {
c.JSON(200, gin.H{"status": "you are logged in"})
@ -812,24 +812,24 @@ func main() {
}
```
Test it with:
测试它:
```sh
$ curl -v --form user=user --form password=password http://localhost:8080/login
```
### XML, JSON and YAML rendering
### XML, JSON 和 YAML 渲染
```go
func main() {
r := gin.Default()
// gin.H is a shortcut for map[string]interface{}
// 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) {
// You also can use a struct
// 你也可以使用一个结构
var msg struct {
Name string `json:"user"`
Message string
@ -838,8 +838,8 @@ func main() {
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// Note that msg.Name becomes "user" in the JSON
// Will output : {"user": "Lena", "Message": "hey", "Number": 123}
// 注意 msg.Name 在 JSON 中会变成 "user"
// 将会输出: {"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
@ -851,36 +851,36 @@ func main() {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
#### SecureJSON
Using SecureJSON to prevent json hijacking. Default prepends `"while(1),"` to response body if the given struct is array values.
使用 SecureJSON 来防止 json 劫持。如果给定的结构体是数组值,默认预置 `"while(1),"` 到 response body 。
```go
func main() {
r := gin.Default()
// You can also use your own secure json prefix
// 你也可以使用自己的安装 json 前缀
// r.SecureJsonPrefix(")]}',\n")
r.GET("/someJSON", func(c *gin.Context) {
names := []string{"lena", "austin", "foo"}
// Will output : while(1);["lena","austin","foo"]
// 将会输出 : while(1);["lena","austin","foo"]
c.SecureJSON(http.StatusOK, names)
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
#### JSONP
Using JSONP to request data from a server in a different domain. Add callback to response body if the query parameter callback exists.
在不同的域中使用 JSONP 从一个服务器请求数据。如果请求参数中存在 callback添加 callback 到 response body 。
```go
func main() {
@ -891,19 +891,19 @@ func main() {
"foo": "bar",
}
//callback is x
// Will output : x({\"foo\":\"bar\"})
//callback x
// 将会输出 : x({\"foo\":\"bar\"})
c.JSONP(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
#### AsciiJSON
Using AsciiJSON to Generates ASCII-only JSON with escaped non-ASCII chracters.
使用 AsciiJSON 为非 ASCII 字符生成仅有 ASCII 字符的 JSON 。
```go
func main() {
@ -915,16 +915,16 @@ func main() {
"tag": "<br>",
}
// will output : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
// 将会输出 : {"lang":"GO\u8bed\u8a00","tag":"\u003cbr\u003e"}
c.AsciiJSON(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
### Serving static files
### 静态文件服务
```go
func main() {
@ -933,12 +933,12 @@ func main() {
router.StaticFS("/more_static", http.Dir("my_file_system"))
router.StaticFile("/favicon.ico", "./resources/favicon.ico")
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
router.Run(":8080")
}
```
### Serving data from reader
### 从 reader 提供数据
```go
func main() {
@ -964,9 +964,9 @@ func main() {
}
```
### HTML rendering
### HTML 渲染
Using LoadHTMLGlob() or LoadHTMLFiles()
使用 LoadHTMLGlob() 或 LoadHTMLFiles()
```go
func main() {
@ -992,7 +992,7 @@ templates/index.tmpl
</html>
```
Using templates with same name in different directories
在不同的目录使用具有相同名称的模板
```go
func main() {
@ -1036,9 +1036,9 @@ templates/users/index.tmpl
{{ end }}
```
#### Custom Template renderer
#### 自定义模板渲染器
You can also use your own html template render
你也可以使用你自己的 HTML 模板渲染
```go
import "html/template"
@ -1051,9 +1051,9 @@ func main() {
}
```
#### Custom Delimiters
#### 自定义分隔符
You may use custom delims
你可以使用自定义分隔符
```go
r := gin.Default()
@ -1061,9 +1061,9 @@ You may use custom delims
r.LoadHTMLGlob("/path/to/templates"))
```
#### Custom Template Funcs
#### 自定义模板函数
See the detail [example code](examples/template).
查看详细的 [示例代码](examples/template).
main.go
@ -1112,13 +1112,13 @@ Result:
Date: 2017/07/01
```
### Multitemplate
### 多模板
Gin allow by default use only one html.Template. Check [a multitemplate render](https://github.com/gin-contrib/multitemplate) for using features like go 1.6 `block template`.
Gin 允许默认只使用一个 html.Template 。查看 [多模板渲染](https://github.com/gin-contrib/multitemplate) 的使用详情,类似 go 1.6 `block template`
### Redirects
### 重定向
Issuing a HTTP redirect is easy. Both internal and external locations are supported.
发出一个 HTTP 重定向非常容易, 同时支持内部和外部地址。
```go
r.GET("/test", func(c *gin.Context) {
@ -1126,8 +1126,7 @@ r.GET("/test", func(c *gin.Context) {
})
```
Issuing a Router redirect, use `HandleContext` like below.
发出路由重定向,使用下面示例中的 `HandleContext`
``` go
r.GET("/test", func(c *gin.Context) {
@ -1140,25 +1139,25 @@ r.GET("/test2", func(c *gin.Context) {
```
### Custom Middleware
### 自定义中间件
```go
func Logger() gin.HandlerFunc {
return func(c *gin.Context) {
t := time.Now()
// Set example variable
// 设置简单的变量
c.Set("example", "12345")
// before request
// 在请求之前
c.Next()
// after request
// 在请求之后
latency := time.Since(t)
log.Print(latency)
// access the status we are sending
// 记录我们的访问状态
status := c.Writer.Status()
log.Println(status)
}
@ -1171,19 +1170,19 @@ func main() {
r.GET("/test", func(c *gin.Context) {
example := c.MustGet("example").(string)
// it would print: "12345"
// 它将打印: "12345"
log.Println(example)
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
### Using BasicAuth() middleware
### 使用 BasicAuth() 中间件
```go
// simulate some private data
// 模拟一些私有的数据
var secrets = gin.H{
"foo": gin.H{"email": "foo@bar.com", "phone": "123433"},
"austin": gin.H{"email": "austin@example.com", "phone": "666"},
@ -1193,8 +1192,8 @@ var secrets = gin.H{
func main() {
r := gin.Default()
// Group using gin.BasicAuth() middleware
// gin.Accounts is a shortcut for map[string]string
// 在组中使用 gin.BasicAuth() 中间件
// gin.Accounts 是 map[string]string 的快捷方式
authorized := r.Group("/admin", gin.BasicAuth(gin.Accounts{
"foo": "bar",
"austin": "1234",
@ -1202,10 +1201,10 @@ func main() {
"manu": "4321",
}))
// /admin/secrets endpoint
// hit "localhost:8080/admin/secrets
// /admin/secrets 结尾
// 点击 "localhost:8080/admin/secrets
authorized.GET("/secrets", func(c *gin.Context) {
// get user, it was set by the BasicAuth middleware
// 获取 user, 它是由 BasicAuth 中间件设置的
user := c.MustGet(gin.AuthUserKey).(string)
if secret, ok := secrets[user]; ok {
c.JSON(http.StatusOK, gin.H{"user": user, "secret": secret})
@ -1214,47 +1213,47 @@ func main() {
}
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
### Goroutines inside a middleware
### 在中间件中使用协成
When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.
在一个中间件或处理器中启动一个新的协成时,你 **不应该** 使用它里面的原始的 context ,只能去使用它的只读副本。
```go
func main() {
r := gin.Default()
r.GET("/long_async", func(c *gin.Context) {
// create copy to be used inside the goroutine
// 创建在协成中使用的副本
cCp := c.Copy()
go func() {
// simulate a long task with time.Sleep(). 5 seconds
// 使用 time.Sleep() 休眠 5 秒,模拟一个用时长的任务。
time.Sleep(5 * time.Second)
// note that you are using the copied context "cCp", IMPORTANT
// 注意,你使用的是复制的 context "cCp" ,重要
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
r.GET("/long_sync", func(c *gin.Context) {
// simulate a long task with time.Sleep(). 5 seconds
// 使用 time.Sleep() 休眠 5 秒,模拟一个用时长的任务。
time.Sleep(5 * time.Second)
// since we are NOT using a goroutine, we do not have to copy the context
// 因为我们没有使用协成,我们不需要复制 context
log.Println("Done! in path " + c.Request.URL.Path)
})
// Listen and serve on 0.0.0.0:8080
// 监听并服务于 0.0.0.0:8080
r.Run(":8080")
}
```
### Custom HTTP configuration
### 自定义 HTTP 配置
Use `http.ListenAndServe()` directly, like this:
直接使用 `http.ListenAndServe()` ,像这样:
```go
func main() {
@ -1262,7 +1261,7 @@ func main() {
http.ListenAndServe(":8080", router)
}
```
or
```go
func main() {
@ -1279,9 +1278,9 @@ func main() {
}
```
### Support Let's Encrypt
### 支持 Let's Encrypt
example for 1-line LetsEncrypt HTTPS servers.
一个 LetsEncrypt HTTPS 服务器的示例。
[embedmd]:# (examples/auto-tls/example1/main.go go)
```go
@ -1297,7 +1296,7 @@ import (
func main() {
r := gin.Default()
// Ping handler
// Ping 处理器
r.GET("/ping", func(c *gin.Context) {
c.String(200, "pong")
})
@ -1306,7 +1305,7 @@ func main() {
}
```
example for custom autocert manager.
自定义 autocert 管理器示例。
[embedmd]:# (examples/auto-tls/example2/main.go go)
```go
@ -1338,9 +1337,9 @@ func main() {
}
```
### Run multiple service using Gin
### 使用 Gin 运行多个服务
See the [question](https://github.com/gin-gonic/gin/issues/346) and try the following example:
查看 [问题](https://github.com/gin-gonic/gin/issues/346) 并尝试下面示例:
[embedmd]:# (examples/multiple-service/main.go go)
```go
@ -1420,12 +1419,12 @@ func main() {
}
```
### Graceful restart or stop
### 正常的重启或停止
Do you want to graceful restart or stop your web server?
There are some ways this can be done.
你想正常的重启或停止你的 web 服务器吗?
有一些方法可以做到。
We can use [fvbock/endless](https://github.com/fvbock/endless) to replace the default `ListenAndServe`. Refer issue [#296](https://github.com/gin-gonic/gin/issues/296) for more details.
我们能使用 [fvbock/endless](https://github.com/fvbock/endless) 去替换默认的 `ListenAndServe`. 参考 issue [#296](https://github.com/gin-gonic/gin/issues/296) 了解更多细节。
```go
router := gin.Default()
@ -1434,13 +1433,13 @@ router.GET("/", handler)
endless.ListenAndServe(":4242", router)
```
An alternative to endless:
另外一些替代方案:
* [manners](https://github.com/braintree/manners): A polite Go HTTP server that shuts down gracefully.
* [graceful](https://github.com/tylerb/graceful): Graceful is a Go package enabling graceful shutdown of an http.Handler server.
* [grace](https://github.com/facebookgo/grace): Graceful restart & zero downtime deploy for Go servers.
* [manners](https://github.com/braintree/manners)  一个有礼貌的 Go HTTP服务器它可以正常的关闭。
* [graceful](https://github.com/tylerb/graceful)  Graceful 是一个 Go 包它可以正常的关闭一个 http.Handler 服务器。
* [grace](https://github.com/facebookgo/grace) : 正常的重启 & Go 服务器零停机部署。
If you are using Go 1.8, you may not need to use this library! Consider using http.Server's built-in [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) method for graceful shutdowns. See the full [graceful-shutdown](./examples/graceful-shutdown) example with gin.
如果你使用的是 Go 1.8你可能不需要使用这些库考虑使用 http.Server 内置的 [Shutdown()](https://golang.org/pkg/net/http/#Server.Shutdown) 方法正常关闭。查看 Gin 中完整的 [graceful-shutdown](./examples/graceful-shutdown) 示例。
[embedmd]:# (examples/graceful-shutdown/graceful-shutdown/server.go go)
```go
@ -1472,14 +1471,13 @@ func main() {
}
go func() {
// service connections
// 连接服务器
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("listen: %s\n", err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with
// a timeout of 5 seconds.
// 等待中断信号超时 5 秒正常关闭服务器
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
<-quit
@ -1494,11 +1492,9 @@ func main() {
}
```
### Build a single binary with templates
### 使用模板构建单个二进制文件
You can build a server into a single binary containing templates by using [go-assets][].
[go-assets]: https://github.com/jessevdk/go-assets
你可以使用 [go-assets](https://github.com/jessevdk/go-assets) 将服务器构建到一个包含模板的单独的二进制文件中。
```go
func main() {
@ -1516,7 +1512,7 @@ func main() {
r.Run(":8080")
}
// loadTemplate loads templates embedded by go-assets-builder
// loadTemplate 加载 go-assets-builder 嵌入的模板
func loadTemplate() (*template.Template, error) {
t := template.New("")
for name, file := range Assets.Files {
@ -1536,11 +1532,11 @@ func loadTemplate() (*template.Template, error) {
}
```
See a complete example in the `examples/assets-in-binary` directory.
在 `examples/assets-in-binary` 中查看一个完成的示例。
### Bind form-data request with custom struct
### 使用自定义结构绑定表单数据请求
The follow example using custom struct:
下面示例使用自定义结构:
```go
type StructA struct {
@ -1601,7 +1597,7 @@ func main() {
}
```
Using the command `curl` command result:
命令行中使用 `curl` 命令的结果:
```
$ curl "http://localhost:8080/getb?field_a=hello&field_b=world"
@ -1612,7 +1608,7 @@ $ curl "http://localhost:8080/getd?field_x=hello&field_d=world"
{"d":"world","x":{"FieldX":"hello"}}
```
**NOTE**: NOT support the follow style struct:
**注意**: 不支持下面风格的结构:
```go
type StructX struct {
@ -1628,12 +1624,11 @@ type StructZ struct {
}
```
In a word, only support nested custom struct which have no `form` now.
总之,只支持当前没有 `form` 嵌套的自定义结构。
### Try to bind body into different structs
### 尝试将 body 绑定到不同的结构中
The normal methods for binding request body consumes `c.Request.Body` and they
cannot be called multiple times.
绑定 request body 的常规方法是使用 `c.Request.Body` 并且不能多次调用它们。
```go
type formA struct {
@ -1647,10 +1642,10 @@ type formB struct {
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
// This c.ShouldBind consumes c.Request.Body and it cannot be reused.
// 这里 c.ShouldBind 使用 c.Request.Body 并且它不能被重复使用。
if errA := c.ShouldBind(&objA); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
// Always an error is occurred by this because c.Request.Body is EOF now.
// 这里总会出现一个错误因为 c.Request.Body 现在是 EOF 。
} else if errB := c.ShouldBind(&objB); errB == nil {
c.String(http.StatusOK, `the body should be formB`)
} else {
@ -1659,19 +1654,20 @@ func SomeHandler(c *gin.Context) {
}
```
For this, you can use `c.ShouldBindBodyWith`.
对于这一点, 你可以使用 `c.ShouldBindBodyWith` 。
```go
func SomeHandler(c *gin.Context) {
objA := formA{}
objB := formB{}
// This reads c.Request.Body and stores the result into the context.
// 这里读取 c.Request.Body 并将结果存储到 context 中。
if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
c.String(http.StatusOK, `the body should be formA`)
// At this time, it reuses body stored in the context.
// 这是它重用存储在 context 中的 body 。
} else if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
c.String(http.StatusOK, `the body should be formB JSON`)
// And it can accepts other formats
// 并且它可以接受其他格式
} else if errB2 := c.ShouldBindBodyWith(&objB, binding.XML); errB2 == nil {
c.String(http.StatusOK, `the body should be formB XML`)
} else {
@ -1680,17 +1676,15 @@ func SomeHandler(c *gin.Context) {
}
```
* `c.ShouldBindBodyWith` stores body into the context before binding. This has
a slight impact to performance, so you should not use this method if you are
enough to call binding at once.
* This feature is only needed for some formats -- `JSON`, `XML`, `MsgPack`,
`ProtoBuf`. For other formats, `Query`, `Form`, `FormPost`, `FormMultipart`,
can be called by `c.ShouldBind()` multiple times without any damage to
performance (See [#1341](https://github.com/gin-gonic/gin/pull/1341)).
* `c.ShouldBindBodyWith` 在绑定前存储 body 到 context 中。这对性能会有轻微的影响,所以如果你可以通过立即调用绑定, 不应该使用这个方法。
### http2 server push
* 只有一些格式需要这个功能 -- `JSON` 、 `XML` 、 `MsgPack`
`ProtoBuf` 。 对于其他格式, `Query``Form``FormPost``FormMultipart`
能被 `c.ShouldBind()` 多次调用,而不会对性能造成任何损害 (参见 [#1341](https://github.com/gin-gonic/gin/pull/1341))。
http.Pusher is supported only **go1.8+**. See the [golang blog](https://blog.golang.org/h2push) for detail information.
### HTTP2 服务器推送
http.Pusher 仅仅被 **go1.8+** 支持。 在 [golang 官方博客](https://blog.golang.org/h2push) 中查看详细信息。
[embedmd]:# (examples/http-pusher/main.go go)
```go
@ -1722,7 +1716,7 @@ func main() {
r.GET("/", func(c *gin.Context) {
if pusher := c.Writer.Pusher(); pusher != nil {
// use pusher.Push() to do server push
// 使用 pusher.Push() 去进行服务器推送
if err := pusher.Push("/assets/app.js", nil); err != nil {
log.Printf("Failed to push: %v", err)
}
@ -1732,14 +1726,14 @@ func main() {
})
})
// Listen and Server in https://127.0.0.1:8080
// 监听并服务于 https://127.0.0.1:8080
r.RunTLS(":8080", "./testdata/server.pem", "./testdata/server.key")
}
```
## Testing
## 测试
The `net/http/httptest` package is preferable way for HTTP testing.
`net/http/httptest` 包是 HTTP 测试的首选方式。
```go
package main
@ -1758,7 +1752,7 @@ func main() {
}
```
Test for code example above:
测试上面代码的示例:
```go
package main
@ -1783,9 +1777,9 @@ func TestPingRoute(t *testing.T) {
}
```
## Users [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
## 使用者 [![Sourcegraph](https://sourcegraph.com/github.com/gin-gonic/gin/-/badge.svg)](https://sourcegraph.com/github.com/gin-gonic/gin?badge)
Awesome project lists using [Gin](https://github.com/gin-gonic/gin) web framework.
使用 [Gin](https://github.com/gin-gonic/gin) web 框架的非常棒的项目列表。
* [drone](https://github.com/drone/drone): Drone is a Continuous Delivery platform built on Docker, written in Go
* [gorush](https://github.com/appleboy/gorush): A push notification server written in Go.
* [drone](https://github.com/drone/drone) Drone 是一个用 Go 编写的基于 Docker 的持续交付平台。
* [gorush](https://github.com/appleboy/gorush) 一个用 Go 编写的消息推送服务器。