1 Star 0 Fork 0

vick/kinfu

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
fmiddlewares.go 10.19 KB
一键复制 编辑 原始数据 按行查看 历史
package fgin
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/prometheus/client_golang/prometheus"
)
func addMiddleWare(r *gin.Engine) {
middleWareLs := []gin.HandlerFunc{recoverMiddleWare(), fginRateLimiterMiddleware(), fginCircuitBreakerMiddleware()}
if (fginConfig.Log != Log{}) {
// 如果日志配置不为空,则自动增加入gin中间件
middleWareLs = append(middleWareLs, logMiddleware())
}
if (fginConfig.Cors != Cors{}) {
// 配置解决跨域
middleWareLs = append(middleWareLs, corsMiddleWare())
}
if err := checkAuthConfig(); err == nil {
// 配置了鉴权则加入中间件
middleWareLs = append(middleWareLs, authMiddleware())
}
if (fginConfig.Jaeger != Jaeger{}) {
// 配置了jaeger则开启链路追踪
if fginConfig.ServiceName == "" {
logSuger.Error("开启jaeger链路追踪,service_name必须配置")
panic(errors.New("开启jaeger链路追踪,service_name必须配置"))
}
middleWareLs = append(middleWareLs, jaegerMiddleWare())
}
if (fginConfig.Prometheus != Prometheus{}) {
// 配置了prometheus则开启链路追踪
if fginConfig.ServiceName == "" {
logSuger.Error("开启prometheus监控,service_name必须配置")
panic(errors.New("开启prometheus监控,service_name必须配置"))
}
middleWareLs = append(middleWareLs, prometheusMiddleWare())
}
// 将响应体暴露出去
middleWareLs = append(middleWareLs, fginResponseMiddleware())
r.Use(middleWareLs...)
}
// 日志中间件
func logMiddleware() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
nowTime := time.Now()
// 记录请求日志
bodyParamsB, _ := io.ReadAll(ctx.Request.Body)
// body只能读取一次,所以要重新写回
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(bodyParamsB))
ctx.Next()
// 耗时
consTime := time.Since(nowTime)
// 处理为紧凑的json
paramsMap := map[string]any{}
_ = json.Unmarshal(bodyParamsB, &paramsMap)
bodyParams, _ := json.Marshal(paramsMap)
logInfo := fmt.Sprintf("ip:%s, code: %v, cons:%v, method:%s, uri:%s, form:%v, body:%s", ctx.ClientIP(), ctx.Writer.Status(), consTime, ctx.Request.Method, ctx.Request.URL, ctx.Request.Form, bodyParams)
defer func() {
if err := recover(); err != nil {
// 捕获程序异常
logErr := fmt.Sprintf("code: 500, cons:%v, method:%s, uri:%s,form:%v, body:%s; err: %v", consTime, ctx.Request.Method, ctx.Request.URL, ctx.Request.Form, bodyParams, err)
logSuger.Error(logErr)
ReturnJson(ctx, 500, nil, errors.New("服务异常,稍后再试"))
// 可以增加一个邮件报警,机器人报警
} else {
if ctx.Request.URL.Path == "/ping" {
// 如果是探针接口,不记录入日志
} else {
logSuger.Info(logInfo)
}
}
}()
}
}
// jwt鉴权中间件
func authMiddleware() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
if ctx.Request.URL.Path == "/ping" {
// 如果是探针接口,不记录入日志
ctx.Next()
return
}
for _, v := range fginConfig.Auth.DisableLs {
// 指定路由,不进行鉴权
// 指定完整路由也不进行鉴权
subRouter := fmt.Sprintf("/%v/", v)
if strings.Contains(ctx.Request.URL.Path, subRouter) || strings.Contains(ctx.Request.URL.Path, v) {
ctx.Next()
return
}
}
token := ctx.GetHeader(fginConfig.Auth.HeaderKey)
if token != "" {
claims, err := ParsearseToken(token)
if err != nil {
ReturnJson(ctx, 401, nil, err)
ctx.Abort()
return
}
for k, v := range claims.AuthData {
ctx.Set(k, v)
}
} else {
ReturnJson(ctx, 401, nil, errors.New("暂未授权"))
ctx.Abort()
return
}
ctx.Next()
}
}
// jaeger链路追踪
func jaegerMiddleWare() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
// 设置请求的上下文
var tracer opentracing.Tracer
if opentracing.IsGlobalTracerRegistered() {
tracer = opentracing.GlobalTracer()
} else {
logSuger.Error("http jaeger GlobalTracer is undefine")
ctx.Next()
}
var span opentracing.Span
header := ctx.Request.Header
spanCtx, err := opentracing.GlobalTracer().Extract(
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(header),
)
if err == nil {
span = opentracing.StartSpan(ctx.Request.URL.Path, opentracing.ChildOf(spanCtx))
} else {
span = tracer.StartSpan(ctx.Request.URL.Path)
}
// 将 span 注入到请求的上下文中
ctx.Request = ctx.Request.WithContext(opentracing.ContextWithSpan(ctx.Request.Context(), span))
// 继续处理请求
ctx.Next()
defer func() {
var responseBody string
if ctx.Writer.Status() < 200 || ctx.Writer.Status() >= 300 {
span.SetTag("error", true)
// 从上下文中获取 CustomResponseWriter
if customWriter, ok := ctx.Get("fginResponse"); ok {
writer := customWriter.(*FginResponseWriter)
// 获取响应状态码和响应体内容
responseBody = writer.body.String()
}
}
span.LogKV("body", responseBody, "method", ctx.Request.Method, "status", ctx.Writer.Status())
span.Finish()
}()
}
}
// 异常抓取中间件
func recoverMiddleWare() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
defer func() {
if err := recover(); err != nil {
if (fginConfig.Log == Log{} || logSuger == nil) {
fmt.Println(err)
} else {
logSuger.Error(err)
}
}
}()
ctx.Next()
}
}
// 跨域中间件
func corsMiddleWare() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
method := ctx.Request.Method
origin := ctx.Request.Header.Get("Origin")
if origin != "" {
switch {
case fginConfig.Cors.UseDefault:
ctx.Header("Access-Control-Allow-Origin", "*") // 可将将 * 替换为指定的域名
ctx.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE") // 允许的请求
ctx.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization") // 允许客户端携带的头
ctx.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type") // 响应暴露头
ctx.Header("Access-Control-Allow-Credentials", "true") //允许客户端携带信息头,比如cookie
ctx.Header("Access-Control-Max-Age", "86400")
default:
// 默认使用自定义配置
originMap := make(map[string]string)
originStr := strings.Replace(fginConfig.Cors.AllowOrigins, " ", "", -1)
for _, v := range strings.Split(originStr, ",") {
originMap[v] = v
}
if v, ok := originMap["*"]; ok {
ctx.Header("Access-Control-Allow-Origin", v) // 如果配置了*,则优先使用
}
if v, ok := originMap[origin]; ok {
ctx.Header("Access-Control-Allow-Origin", v) // 可将将 * 替换为指定的域名
}
ctx.Header("Access-Control-Allow-Methods", fginConfig.Cors.AllowMethods) // 允许的请求
ctx.Header("Access-Control-Allow-Headers", fginConfig.Cors.AllowHeaders) // 允许客户端携带的头
ctx.Header("Access-Control-Expose-Headers", fginConfig.Cors.ExposeHeaders) // 响应暴露头
ctx.Header("Access-Control-Allow-Credentials", fmt.Sprintf("%v", fginConfig.Cors.AllowCredentials)) //允许客户端携带信息头,比如cookie
ctx.Header("Access-Control-Max-Age", fmt.Sprintf("%v", fginConfig.Cors.MaxAge))
}
}
if method == "OPTIONS" {
ctx.AbortWithStatus(http.StatusNoContent)
}
ctx.Next()
}
}
// prometheus 中间件
func prometheusMiddleWare() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
defer func() {
// 计数
requestCount.WithLabelValues(ctx.Request.Method, ctx.Request.URL.Path, fmt.Sprintf("%v", ctx.Writer.Status())).Inc()
// 耗时
obs := requestHistogram.WithLabelValues(ctx.Request.Method, ctx.Request.URL.Path, fmt.Sprintf("%v", ctx.Writer.Status()))
timer := prometheus.NewTimer(obs)
timer.ObserveDuration()
}()
ctx.Next()
}
}
// 定义一个自定义的 ResponseWriter,以便我们可以捕获响应数据
type FginResponseWriter struct {
gin.ResponseWriter
body *bytes.Buffer
}
func (w *FginResponseWriter) Write(b []byte) (int, error) {
// 捕获响应数据到 buffer 中
w.body.Write(b)
// 调用原始的 Write 方法
return w.ResponseWriter.Write(b)
}
// 中间件函数,用于创建 CustomResponseWriter,将响应体记录到上下文
func fginResponseMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
// 创建一个自定义的 ResponseWriter
customWriter := &FginResponseWriter{
ResponseWriter: ctx.Writer,
body: bytes.NewBufferString(""),
}
// 将自定义 Writer 替换默认的 ResponseWriter
ctx.Writer = customWriter
// 继续处理请求
ctx.Next()
// 将自定义 Writer 存储到上下文中,以便后续中间件访问
ctx.Set("fginResponse", customWriter)
}
}
// 限流器中间件
func fginRateLimiterMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
if err := rateLimiter.Allow(); err != nil {
logSuger.Info(fginConfig.ServiceName + ":" + ctx.Request.URL.Path + rateErrMsg)
ReturnJson(ctx, rateCode, "", errors.New(fginConfig.ServiceName+":"+ctx.Request.URL.Path+rateErrMsg))
ctx.Abort()
return
}
ctx.Next()
}
}
// 熔断中间件
func fginCircuitBreakerMiddleware() gin.HandlerFunc {
return func(ctx *gin.Context) {
if err := circuitBreaker.Allow(); err != nil {
logSuger.Info(fginConfig.ServiceName + ":" + ctx.Request.URL.Path + circuitErrMsg)
ReturnJson(ctx, circuitCode, "", errors.New(fginConfig.ServiceName+":"+ctx.Request.URL.Path+circuitErrMsg))
ctx.Abort()
return
}
ctx.Next()
// 500之间状态码,定义为可熔断
if ctx.Writer.Status() < 600 || ctx.Writer.Status() >= 500 {
// 标记失败
circuitBreaker.MarkFailed()
} else {
// 标记成功
circuitBreaker.MarkSuccess()
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/wu-jin-feng/kinfu.git
git@gitee.com:wu-jin-feng/kinfu.git
wu-jin-feng
kinfu
kinfu
63ab2dd32d62

搜索帮助

0d507c66 1850385 C8b1a773 1850385