1 Star 1 Fork 0

ayun2001 / ok-boost-go

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
middleware.go 8.28 KB
一键复制 编辑 原始数据 按行查看 历史
李盛雁 提交于 2023-10-10 17:51 . 开发中
package server
import (
"net"
"net/http"
"os"
"runtime/debug"
"strings"
"sync"
"time"
boost "gitee.com/ayun2001/ok-boost-go"
"gitee.com/ayun2001/ok-boost-go/http/server/common"
"gitee.com/ayun2001/ok-boost-go/pkg/httptool"
"gitee.com/ayun2001/ok-boost-go/pkg/utils"
"github.com/bwmarrin/snowflake"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// 解决跨站
func ginCors() gin.HandlerFunc {
return func(c *gin.Context) {
method := c.Request.Method
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
c.Header("Access-Control-Allow-Headers", "*")
// c.Header("Access-Control-Allow-Headers", "Content-Type, AccessToken, X-CSRF-Token, Authorization, Token")
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
c.Header("Access-Control-Allow-Credentials", "true")
//设置缓存时间
c.Header("Access-Control-Max-Age", "172800")
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
}
// 下一个
c.Next()
}
}
func ginAccessLogger(l *zap.SugaredLogger, recordContextBody bool) gin.HandlerFunc {
return func(c *gin.Context) {
// 设置日志指针
c.Set(common.ConstRequestLoggerKey, l)
// 跳过不需要记录的路径
if common.SkipResources(c) {
c.Next()
// 回收日志读取对象指针
c.Set(common.ConstRequestLoggerKey, nil)
return
}
var requestBody, responseBody string
// 正常处理系统日志
start := time.Now()
path := GenerateRequestPath(c)
requestContentType := httptool.StringFilterFlags(c.Request.Header.Get(httptool.ConstHttpHeaderContentType))
if recordContextBody && httptool.CanRecordContextBody(c.Request.Header) {
requestBody = GenerateRequestBody(c)
}
// 下一个
c.Next()
// response 返回
if len(c.Errors) > 0 {
for _, e := range c.Errors.Errors() {
l.Error(e)
}
} else {
latency := time.Since(start)
responseHeader := c.Writer.Header()
responseContentType := httptool.StringFilterFlags(responseHeader.Get(httptool.ConstHttpHeaderContentType))
if recordContextBody && httptool.CanRecordContextBody(responseHeader) {
responseBody = GenerateResponseBody(c)
}
reqMetadata := common.GetRequestMetadata(c)
l.Infow(
"http server access log",
"requestID", c.GetHeader(common.ConstHttpRequestID),
"clientIP", reqMetadata.ClientIP,
"clientEndpoint", c.Request.RemoteAddr,
"path", path,
"method", c.Request.Method,
"status", c.Writer.Status(),
"latency", latency.String(),
"userAgent", c.Request.UserAgent(),
"requestContentType", requestContentType,
"requestQuery", c.Request.URL.RawQuery,
"requestBody", requestBody,
"responseContentType", responseContentType,
"responseBody", responseBody,
)
}
// 回收日志读取对象指针
c.Set(common.ConstRequestLoggerKey, nil)
}
}
// ginSystemRecovery recover 掉项目可能出现的panic,并使用zap记录相关日志
func ginSystemRecovery() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
// Check for a broken connection, as it is not really a
// condition that warrants a panic f trace.
var brokenPipe bool
if ne, ok := err.(*net.OpError); ok {
if se, ok := ne.Err.(*os.SyscallError); ok {
errMessage := strings.ToLower(se.Error())
if strings.Contains(errMessage, "broken pipe") || strings.Contains(errMessage, "connection reset by peer") {
brokenPipe = true
}
}
}
if brokenPipe {
boost.Logger.Error("broken connection", zap.Any("error", err))
} else {
path := GenerateRequestPath(c)
body := GenerateRequestBody(c)
requestContentType := httptool.StringFilterFlags(c.Request.Header.Get(httptool.ConstHttpHeaderContentType))
reqMetadata := common.GetRequestMetadata(c)
boost.Logger.Errorw(
"http server recovery from panic",
"requestID", c.GetHeader(common.ConstHttpRequestID),
"clientIP", reqMetadata.ClientIP,
"clientEndpoint", c.Request.RemoteAddr,
"path", path,
"status", c.Writer.Status(),
"method", c.Request.Method,
"userAgent", c.Request.UserAgent(),
"requestContentType", requestContentType,
"requestQuery", c.Request.URL.RawQuery,
"requestBody", body,
"error", err,
"stack", utils.BytesToString(debug.Stack()),
)
}
// If the connection is dead, we can't write a status to it.
if brokenPipe {
_ = c.Error(err.(error)) // nolint: errcheck
c.Abort()
} else {
//c.AbortWithStatus(http.StatusInternalServerError)
// 输出到内容到 http
c.AbortWithStatusJSON(http.StatusInternalServerError, httptool.BaseHttpResponse{
Code: http.StatusInternalServerError,
ErrorMessage: http.StatusText(http.StatusInternalServerError),
ErrorDetail: "http server internal error, method: " + c.Request.Method + ", path: " + c.Request.URL.Path,
})
}
}
}()
// 下一个
c.Next()
}
}
func ginRequestID() gin.HandlerFunc {
return func(c *gin.Context) {
rid := c.GetHeader(common.ConstHttpRequestID)
if len(rid) <= 0 {
if o, ok := c.Get(common.ConstRequestIdGeneratorKey); ok {
rid = o.(*snowflake.Node).Generate().String()
} else {
rid = utils.GetRandIdString() // 效能差,竞争全局锁
}
c.Request.Header.Add(common.ConstHttpRequestID, rid)
}
c.Header(common.ConstHttpRequestID, rid)
// 下一个
c.Next()
}
}
func ginRequestBodyBuffer(p *sync.Pool) gin.HandlerFunc {
return func(c *gin.Context) {
var b *common.ContextBodyBuff
// 创建缓存对象
b = p.Get().(*common.ContextBodyBuff)
b.GetBuffer().Reset()
c.Set(common.ConstRequestBodyBufferKey, b)
// 下一个
c.Next()
// 归还缓存对象
if o, ok := c.Get(common.ConstRequestBodyBufferKey); ok {
b = o.(*common.ContextBodyBuff)
b.GetBuffer().Reset() // bytes.Buffer 要 reset,但是 slice 就不能,这个做 io.CopyBuffer 用的
p.Put(o) // 归还对象
c.Set(common.ConstRequestBodyBufferKey, nil) // 释放指向 bodyBuff 对象
c.Request.Body = nil // 释放指向创建的 io.NopCloser 对象
}
}
}
func ginRequestIdGenerator(p *sync.Pool) gin.HandlerFunc {
return func(c *gin.Context) {
// 创建缓存对象
c.Set(common.ConstRequestIdGeneratorKey, p.Get())
// 下一个请求
c.Next()
// 归还缓存对象
if o, ok := c.Get(common.ConstRequestIdGeneratorKey); ok {
p.Put(o)
c.Set(common.ConstRequestIdGeneratorKey, nil)
}
}
}
func ginRequestMetaData(p *sync.Pool, extOpts *map[string]interface{}) gin.HandlerFunc {
endpoint := (*extOpts)[common.ConstPromServiceEndpoint].(string)
return func(c *gin.Context) {
// 创建缓存对象
d := p.Get().(*common.RequestMetadata)
d.Host = c.Request.Host
d.ClientIP = c.ClientIP()
d.Listener = endpoint
d.OriginWriter = c.Writer
c.Set(common.ConstRequestMetadataKey, d)
// 下一个
c.Next()
// 归还缓存对象
if o, ok := c.Get(common.ConstRequestMetadataKey); ok {
p.Put(o) // 归还对象
c.Set(common.ConstRequestMetadataKey, nil) // 释放指向 requestMetadata 对象
}
}
}
func ginResponseBodyBuffer(p *sync.Pool) gin.HandlerFunc {
return func(c *gin.Context) {
var b *common.ContextBodyBuff
// 创建缓存对象
b = p.Get().(*common.ContextBodyBuff)
b.GetBuffer().Reset()
c.Set(common.ConstResponseBodyBufferKey, b)
// metadata
contextMetadata := common.GetRequestMetadata(c)
// 覆盖原有 writer
wr := common.NewResponseBodyWriter(contextMetadata.OriginWriter, b.GetBuffer())
c.Writer = wr
// 下一个
c.Next()
// 重置,防止下次请求时,上次的数据影响
wr.Reset()
// 还原原有 writer
c.Writer = contextMetadata.OriginWriter
// 归还缓存对象
if o, ok := c.Get(common.ConstResponseBodyBufferKey); ok {
b = o.(*common.ContextBodyBuff)
b.GetBuffer().Reset() // bytes.Buffer 要 reset,但是 slice 就不能,这个做 io.CopyBuffer 用的
p.Put(o) // 归还对象
c.Set(common.ConstResponseBodyBufferKey, nil) // 释放指向 bodyBuff 对象
}
}
}
Go
1
https://gitee.com/ayun2001/ok-boost-go.git
git@gitee.com:ayun2001/ok-boost-go.git
ayun2001
ok-boost-go
ok-boost-go
d07672de674d

搜索帮助