本文介绍gin的一些知识点,如自定义Response,中间件等。
gin
Gin 是一个 go 写的 web 框架,具有高性能的优点。
初级的使用方式不介绍了,具体请查阅官方文档。官方地址:https://github.com/gin-gonic/gin
以下介绍基于gin开发项目的一些常用模块。
自定义Response
每个公司都会自定义接口的数据结构。故我们需要基于Json()
自定义一个更方便好用的response
1 | // Response 数据结构体 |
封装gin.Context以自定义一些方便的方法
1 | // Wrapper include context |
使用:
1 | package main |
通过go run main.go
运行后,浏览器访问localhost:8088
中间件
介绍一些常用的中间件,如跨域、Jwt校验、请求日志等。
备注
引入中间件比如在注册路由之前,谨记!跨域中间件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22package middleware
import (
"github.com/gin-gonic/gin"
)
// CORS 跨域中间件
func CORS(ctx *gin.Context) {
method := ctx.Request.Method
// set response header
ctx.Header("Access-Control-Allow-Origin", ctx.Request.Header.Get("Origin"))
ctx.Header("Access-Control-Allow-Credentials", "true")
ctx.Header("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With")
ctx.Header("Access-Control-Allow-Methods", "GET,POST,PUT,PATCH,DELETE,OPTIONS")
// 默认过滤这两个请求,使用204(No Content)这个特殊的http status code
if method == "OPTIONS" || method == "HEAD" {
ctx.AbortWithStatus(204)
return
}
ctx.Next()
}使用如下:
1
2
3
4
5
6
7
8
9func main() {
router := gin.Default()
router.Use(CORS)
router.GET("/", func(ctx *gin.Context) {
WrapContext(ctx).Success("hello,world")
})
router.Run(":8088")
}Jwt校验
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84package main
import (
"errors"
"github.com/dgrijalva/jwt-go"
"github.com/gin-gonic/gin"
"strings"
"time"
)
var (
TokenNotExist = errors.New("token not exist")
TokenValidateFailed = errors.New("token validate failed")
ClaimsKey = "uniqueClaimsKey"
SignKey = "test"
)
// JwtAuth jwt
type JwtAuth struct {
SignKey []byte
}
// ParseToken parse token
func (jwtAuth JwtAuth) ParseToken(token string) (jwt.Claims, error) {
tokenClaims, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
return jwtAuth.SignKey, nil
})
if err != nil {
return nil, err
}
if tokenClaims.Claims == nil || !tokenClaims.Valid {
return nil, TokenValidateFailed
}
return tokenClaims.Claims, nil
}
// GenerateToken
func (jwtAuth JwtAuth) GenerateToken(tokenExpireTime int64 /* 过期时间 */, iss string /* key*/) (string, error) {
now := time.Now().Unix()
exp := now + tokenExpireTime
claim := jwt.MapClaims{
"iss": iss,
"iat": now,
"exp": exp,
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
tokenStr, err := token.SignedString(jwtAuth.SignKey)
return tokenStr, err
}
// JWT gin的jwt中间件
func JWT(ctx *gin.Context) {
// 解析token
if err := validateToken(ctx); err != nil {
WrapContext(ctx).Error(401, err.Error())
ctx.Abort()
return
}
ctx.Next()
}
// validateToken 验证token
func validateToken(ctx *gin.Context) error {
// 获取token
tokenStr := ctx.GetHeader("Authorization")
kv := strings.Split(tokenStr, " ")
if len(kv) != 2 || kv[0] != "Bearer" {
return TokenNotExist
}
jwtAuth := &JwtAuth{SignKey: []byte(SignKey)}
claims, err := jwtAuth.ParseToken(kv[1])
if err != nil {
return err
}
// token存入context
ctx.Set(ClaimsKey, claims)
return nil
}
使用如下:
1 | func main() { |
请求测试:
1 | curl "localhost:8088/user/info" |
- 请求日志
记录每个请求的重要信息1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82import (
"bytes"
"fmt"
"github.com/gin-gonic/gin"
"io/ioutil"
"log"
"net/http"
"time"
)
// bodyLogWriter 定义一个存储响应内容的结构体
type bodyLogWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
// Write 读取响应数据
func (w bodyLogWriter) Write(b []byte) (int, error) {
w.body.Write(b)
return w.ResponseWriter.Write(b)
}
// RequestLog gin的请求日志中间件
func RequestLog(c *gin.Context) {
// 记录请求开始时间
t := time.Now()
blw := &bodyLogWriter{body: bytes.NewBufferString(""), ResponseWriter: c.Writer}
// 必须!
c.Writer = blw
// 获取请求信息
requestBody := getRequestBody(c)
c.Next()
// 记录请求所用时间
latency := time.Since(t)
// 获取响应内容
responseBody := blw.body.String()
logContext := make(map[string]interface{})
// 日志格式
logContext["request_uri"] = c.Request.RequestURI
logContext["request_method"] = c.Request.Method
logContext["refer_service_name"] = c.Request.Referer()
logContext["refer_request_host"] = c.ClientIP()
logContext["request_body"] = requestBody
logContext["request_time"] = t.String()
logContext["response_body"] = responseBody
logContext["time_used"] = fmt.Sprintf("%v", latency)
logContext["header"] = c.Request.Header
log.Println(logContext)
}
// getRequestBody 获取请求参数
func getRequestBody(c *gin.Context) interface{} {
switch c.Request.Method {
case http.MethodGet:
return c.Request.URL.Query()
case http.MethodPost:
fallthrough
case http.MethodPut:
fallthrough
case http.MethodPatch:
var bodyBytes []byte // 我们需要的body内容
// 可以用buffer代替ioutil.ReadAll提高性能
bodyBytes, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
return nil
}
// 将数据还回去
c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(bodyBytes))
return string(bodyBytes)
}
return nil
}
使用
1 | router.Use(ReqeustLog) |
今天就到这儿吧,还有一些比如全局ID中间件,后面来写。
- 本文作者: Hongker
- 本文链接: https://hongker.github.io/2020/04/01/golang-gin/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!