# Gin

#### Gin

* 官方：[Github gin-gonic/gin](https://github.com/gin-gonic/gin)
* 下载：`go get -u github.com/gin-gonic/gin`
* 导入：`import "github.com/gin-gonic/gin"`

***

### 基本使用

#### 启动服务

```go
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	r := gin.Default()
  // 定义路由
	r.GET("hello", func(c *gin.Context) {
		c.String(http.StatusOK, "hello")
	})
	// r.Run() // 默认监听8080
	r.Run(":8080") // 指定监听端口
}
```

#### 获取请求信息

```go
// 获取请求对象
request := c.Request

// 获取请求方法信息
// 此方法返回string类型
method := request.Method
fmt.Println(method)

// 获取请求uri信息
// 此方法返回 *url.URL 类型，可以获取URL中的所有信息
var url *url.URL = request.URL
fmt.Println(url)

// 获取请求头信息
// 此方法返回 http.Header 类型，可获取请求头中的所有信息
header := request.Header
fmt.Println(header)

// 获取请求体信息
// 此方法返回 io.ReadCloser 类型，可以从流中读取请求体数据
body := request.Body
fmt.Println(body)
```

#### 处理不同的请求方法

```go
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)
```

#### 路径参数

必选的路径参数用`:`表示，`name`必须存在，否则不会匹配：

```go
router.GET("/user/:name", func(c *gin.Context) {
  name := c.Param("name")
  c.String(http.StatusOK, "Hello %s", name)
})
```

可选的路径参数用`*`表示，可以匹配`/user/lisi`和`/user/lisi/play`：

```go
router.GET("/user/:name/*action", func(c *gin.Context) {
  name := c.Param("name")
  action := c.Param("action")
  message := name + " is " + action
  c.String(http.StatusOK, message)
})
```

#### 查询参数

```go
router.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)
})
```

#### Multipart/Urlencoded Form 表单处理

```go
router.POST("/form_post", func(c *gin.Context) {
  message := c.PostForm("message") // 获取表单字段，message
  nick := c.DefaultPostForm("nick", "anonymous") // 获取表单字段 nick，并给予缺省值

  c.JSON(200, gin.H{
    "status":  "posted",
    "message": message,
    "nick":    nick,
  })
})
```

#### 表单/查询参数数据转换map

```http
POST /post?ids[a]=1234&ids[b]=hello HTTP/1.1
Content-Type: application/x-www-form-urlencoded

names[first]=thinkerou&names[second]=tianou
```

```go
router.POST("/post", func(c *gin.Context) {
  ids := c.QueryMap("ids")
  names := c.PostFormMap("names")

  fmt.Printf("ids: %v; names: %v", ids, names)
  // ids: map[b:hello a:1234]; names: map[second:tianou first:thinkerou]
})
```

#### 文件上传

**一次处理单个文件：**

```shell
curl -X POST http://localhost:8080/upload \
  -F "file=@/Users/appleboy/test.zip" \
  -H "Content-Type: multipart/form-data"
```

```go
// Set a lower memory limit for multipart forms (default is 32 MiB)
// 设置表单大小限制
router.MaxMultipartMemory = 8 << 20  // 8 MiB
router.POST("/upload", func(c *gin.Context) {
    // 从form中取出指定的文件字段
    file, _ := c.FormFile("file")
    log.Println(file.Filename)

    // 将文件保存到指定路径
    c.SaveUploadedFile(file, dst)

    c.String(http.StatusOK, fmt.Sprintf("'%s' uploaded!", file.Filename))
  })
```

**一次处理多个文件：**

```shell
curl -X POST http://localhost:8080/upload \
  -F "upload[]=@/Users/appleboy/test1.zip" \
  -F "upload[]=@/Users/appleboy/test2.zip" \
  -H "Content-Type: multipart/form-data"
```

```go
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)))
	})
```

### Gin的中间件

Gin默认启动的路由（`gin.Default()`）自带集成两个中间件：

1. `Logger` 日志处理
2. `Recovery` 错误处理

#### 启动一个不带中间件的路由

```go
r := gin.New()
```

#### 启用某些中间件

```go
r := gin.New()
r.Use(gin.Logger())
r.Use(gin.Recovery())
```

#### Logger全局中间件

**日志写入文件：**

```go
func main() {
  
    // Logging to a file.
    f, _ := os.Create("gin.log")
    gin.DefaultWriter = io.MultiWriter(f)

    // Use the following code if you need to write the logs to file and console at the same time.
    // gin.DefaultWriter = io.MultiWriter(f, os.Stdout)

    router := gin.Default()
    router.GET("/ping", func(c *gin.Context) {
        c.String(200, "pong")
    })

    router.Run(":8080")
}
```

**自定义日志格式：**

```go
func main() {
	router := gin.New()

	// LoggerWithFormatter middleware will write the logs to gin.DefaultWriter
	// By default gin.DefaultWriter = os.Stdout
	router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {

		// your custom format
		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")
}
```

**日志着色：**

```go
// 默认情况下，会根据选择的tty对日志进行着色
// 禁用日志着色
gin.DisableConsoleColor()

// 始终启用日志着色
gin.ForceConsoleColor()
```

#### Recovery全局中间件

### 路由分组

```go
v1 := router.Group("/v1")
	{
		v1.POST("/login", loginEndpoint)
		v1.POST("/submit", submitEndpoint)
		v1.POST("/read", readEndpoint)
	}

	// Simple group: v2
	v2 := router.Group("/v2")
	{
		v2.POST("/login", loginEndpoint)
		v2.POST("/submit", submitEndpoint)
		v2.POST("/read", readEndpoint)
	}
```

#### 认证分组

```go
func main() {
	// Creates a router without any middleware by default
	r := gin.New()

	// Global middleware
	// Logger middleware will write the logs to gin.DefaultWriter even if you set with GIN_MODE=release.
	// By default gin.DefaultWriter = os.Stdout
	r.Use(gin.Logger())

	// Recovery middleware recovers from any panics and writes a 500 if there was one.
	r.Use(gin.Recovery())

	// Per route middleware, you can add as many as you desire.
	r.GET("/benchmark", MyBenchLogger(), benchEndpoint)

	// Authorization group
	// authorized := r.Group("/", AuthRequired())
	// exactly the same as:
	authorized := r.Group("/")
	// per group middleware! in this case we use the custom created
	// AuthRequired() middleware just in the "authorized" group.
	authorized.Use(AuthRequired())
	{
		authorized.POST("/login", loginEndpoint)
		authorized.POST("/submit", submitEndpoint)
		authorized.POST("/read", readEndpoint)

		// nested group
		testing := authorized.Group("testing")
		testing.GET("/analytics", analyticsEndpoint)
	}

	// Listen and serve on 0.0.0.0:8080
	r.Run(":8080")
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/golang/web-bian-cheng/gin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
