# Day11 Gin框架之快速指南
Gin
轻量级Web框架,简单易用,中文文档 (opens new window)齐全
# Gin框架入门
- 安装依赖
go get -u github.com/gin-gonic/gin
- 快速入门
package main
import "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 上启动服务
}
# Gin框架参数校验
package blog
import (
"achilles/logger"
"encoding/json"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
func querystringHandler(c *gin.Context) {
username := c.DefaultQuery("username", "linda") // 默认 "linda"
address := c.Query("address") // 默认零值
c.JSON(http.StatusOK, gin.H{
"username": username,
"address": address,
})
}
func formHandler(c *gin.Context) {
// DefaultPostForm取不到值时会返回指定的默认值
username := c.DefaultPostForm("username", "linda") // 默认 "linda"
address := c.PostForm("address") // 默认零值
c.JSON(http.StatusOK, gin.H{
"username": username,
"address": address,
})
}
func jsonHandler(c *gin.Context) {
// 注意:下面为了举例子方便,暂时忽略了错误处理
b, _ := c.GetRawData() // 从c.Request.Body读取请求数据
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)
fmt.Printf("type: %T\nvalue:%#v\n", m, m)
logger.Logger.Info("JSON数据", zap.Any("m", m))
c.JSON(http.StatusOK, m)
}
func pathHandler(c *gin.Context) {
username := c.Param("username")
address := c.Param("address")
c.JSON(http.StatusOK, gin.H{
"username": username,
"address": address,
})
}
type Login struct {
// form:"user" 校验 FormPost提交数据
// json:"user" 校验json提交数据
// binding:"required" 如果没有改选项,默认零值
Uesr string `form:"user" json:"user" binding:"required"`
Password string `json:"password"`
}
func shoudbindHandler(c *gin.Context) {
var login Login
// ShouldBind()会根据请求的Content-Type自行选择绑定器
if err := c.ShouldBind(&login); err != nil {
c.JSON(http.StatusOK, gin.H{
"code": 1,
"msg": "参数错误",
})
logger.Logger.Error("参数错误",
zap.String("path", c.Request.URL.Path),
zap.String("error", err.Error()))
return
}
c.JSON(http.StatusOK, gin.H{
"username": login.Uesr,
"password": login.Password,
})
}
ShouldBind
会按照下面的顺序解析请求中的数据完成绑定:
- 如果是 GET 请求,只使用 Form 绑定引擎(query);
- 如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用 Form(form-data);
# 路由组与路由重定向
func Routers(e *gin.Engine) {
// 路由组
group := e.Group("/blog")
{
group.GET("/querystring", querystringHandler)
group.GET("/path/:username/:address", pathHandler)
group.POST("/form", formHandler)
group.POST("/json", jsonHandler)
group.GET("/shoudbind", shoudbindHandler)
// HTTP重定向
group.GET("/redirect1", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
// 路由重定向
group.GET("/redirect2", func(c *gin.Context) {
c.Request.URL.Path = "/querystring"
e.HandleContext(c)
})
}
}
# Gin框架中间件
- Gin框架可以在处理请求的过程中,加入钩子(Hook)函数,这个钩子函数就叫中间件;
- 中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等;
# 定义中间件
Gin中的中间件必须是一个gin.HandlerFunc
类型
package middleware
import (
"achilles/logger"
"bytes"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// MiddlewareRequetCost 记录接口耗时的中间件
func MiddlewareRequetCost(c *gin.Context) {
start := time.Now()
c.Set("name", "linda") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// 调用该请求的剩余处理程序
c.Next()
// 不调用该请求的剩余处理程序
// c.Abort()
// 计算耗时
cost := time.Since(start)
logger.Logger.Info("耗时统计", zap.Any("cost", cost), zap.String("path", c.Request.URL.Path))
}
type bodyLogWriter struct {
gin.ResponseWriter // 嵌入gin框架ResponseWriter
body *bytes.Buffer // 我们记录用的response
}
// Write 写入响应体数据
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b) // 我们记录一份
return w.ResponseWriter.Write(b) // 真正写入响应
}
// GinBodyLogMiddleware 一个记录返回给客户端响应体的中间件
// https://stackoverflow.com/questions/38501325/how-to-log-response-body-in-gin
func GinBodyLogMiddleware(c *gin.Context) {
blw := &bodyLogWriter{body: bytes.NewBuffer([]byte{}), ResponseWriter: c.Writer}
c.Writer = blw // 使用我们自定义的类型替换默认的
c.Next() // 执行业务逻辑
// 事后按需记录返回的响应
logger.Logger.Info("响应数据", zap.String("body", blw.body.String()))
}
# 注册中间
// 注册一个全局中间件
func Init() *gin.Engine {
// r := gin.Default()
r := gin.New()
// 注册一个全局中间件
r.Use(middleware.MiddlewareRequetCost, middleware.GinBodyLogMiddleware)
for _, opt := range options {
opt(r)
}
return r
}
// 还可以注册到 group 或者 具体的handler