package core import ( "bytes" stdctx "context" "fmt" "io/ioutil" "mime/multipart" "net/http" "net/url" "path" "strings" "sync" "bindbox-game/internal/pkg/logger" "bindbox-game/internal/pkg/trace" "bindbox-game/internal/proposal" "github.com/gin-gonic/gin" "github.com/gin-gonic/gin/binding" ) type HandlerFunc func(c Context) type Trace = trace.T const ( _Alias = "_alias_" _TraceName = "_trace_" _LoggerName = "_logger_" _BodyName = "_body_" _PayloadName = "_payload_" _SessionUserInfo = "_session_user_info" _AbortErrorName = "_abort_error_" _IsRecordMetrics = "_is_record_metrics_" ) var contextPool = &sync.Pool{ New: func() interface{} { return new(context) }, } func newContext(ctx *gin.Context) Context { context := contextPool.Get().(*context) context.ctx = ctx return context } func releaseContext(ctx Context) { c := ctx.(*context) c.ctx = nil contextPool.Put(c) } var _ Context = (*context)(nil) type Context interface { init() // ShouldBindQuery 反序列化 querystring // tag: `form:"xxx"` (注:不要写成 query) ShouldBindQuery(obj interface{}) error // ShouldBindPostForm 反序列化 postform (querystring会被忽略) // tag: `form:"xxx"` ShouldBindPostForm(obj interface{}) error // ShouldBindForm 同时反序列化 querystring 和 postform; // 当 querystring 和 postform 存在相同字段时,postform 优先使用。 // tag: `form:"xxx"` ShouldBindForm(obj interface{}) error // ShouldBindJSON 反序列化 postjson // tag: `json:"xxx"` ShouldBindJSON(obj interface{}) error // ShouldBindXML 反序列化 xml // tag: `xml:"xxx"` ShouldBindXML(obj interface{}) error // ShouldBindURI 反序列化 path 参数(如路由路径为 /user/:name) // tag: `uri:"xxx"` ShouldBindURI(obj interface{}) error // Redirect 重定向 Redirect(code int, location string) // Trace 获取 Trace 对象 Trace() Trace setTrace(trace Trace) disableTrace() // Logger 获取 Logger 对象 Logger() logger.CustomLogger setLogger(logger logger.CustomLogger) // Payload 正确返回 Payload(payload interface{}) getPayload() interface{} // File 文件下载 File(filePath string) // HTML 返回界面 HTML(name string, obj interface{}) // String 返回字符串 String(str string) // XML 返回 XML XML(obj interface{}) // ExcelData 返回二进制数据 ExcelData(filename string, byteData []byte) // FormFile 通过表单上传文件 FormFile(name string) (*multipart.FileHeader, error) // SaveUploadedFile 将表单文件上传到本地 SaveUploadedFile(file *multipart.FileHeader, dst string) error // AbortWithError 错误返回 AbortWithError(err BusinessError) abortError() BusinessError // Header 获取 Header 对象 Header() http.Header // GetHeader 获取 Header GetHeader(key string) string // SetHeader 设置 Header SetHeader(key, value string) // SessionUserInfo 当前用户信息 SessionUserInfo() proposal.SessionUserInfo setSessionUserInfo(info proposal.SessionUserInfo) // Alias 设置路由别名 for metrics path Alias() string setAlias(path string) // disableRecordMetrics 设置禁止记录指标 disableRecordMetrics() ableRecordMetrics() isRecordMetrics() bool // RequestInputParams 获取所有参数 RequestInputParams() url.Values // RequestPostFormParams 获取 PostForm 参数 RequestPostFormParams() url.Values // RequestPathParams 获取路由参数的值 RequestPathParams(key string) string // Request 获取 Request 对象 Request() *http.Request // RawData 获取 Request.Body RawData() []byte // Method 获取 Request.Method Method() string // Host 获取 Request.Host Host() string // Path 获取 请求的路径 Request.URL.Path (不附带 querystring) Path() string // URI 获取 unescape 后的 Request.URL.RequestURI() URI() string // RequestContext 获取请求的 context (当 client 关闭后,会自动 canceled) RequestContext() StdContext // ResponseWriter 获取 ResponseWriter 对象 ResponseWriter() gin.ResponseWriter // Param 获取 URL 参数 Param(key string) string } type context struct { ctx *gin.Context } type StdContext struct { stdctx.Context Trace logger.CustomLogger } func (c *context) init() { body, err := c.ctx.GetRawData() if err != nil { panic(err) } c.ctx.Set(_BodyName, body) // cache body是为了trace使用 c.ctx.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) // re-construct req body } // ShouldBindQuery 反序列化querystring // tag: `form:"xxx"` (注:不要写成query) func (c *context) ShouldBindQuery(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.Query) } // ShouldBindPostForm 反序列化 postform (querystring 会被忽略) // tag: `form:"xxx"` func (c *context) ShouldBindPostForm(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.FormPost) } // ShouldBindForm 同时反序列化querystring和postform; // 当querystring和postform存在相同字段时,postform优先使用。 // tag: `form:"xxx"` func (c *context) ShouldBindForm(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.Form) } // ShouldBindJSON 反序列化postjson // tag: `json:"xxx"` func (c *context) ShouldBindJSON(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.JSON) } // ShouldBindXML 反序列化xml // tag: `xml:"xxx"` func (c *context) ShouldBindXML(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.XML) } // ShouldBindURI 反序列化path参数(如路由路径为 /user/:name) // tag: `uri:"xxx"` func (c *context) ShouldBindURI(obj interface{}) error { return c.ctx.ShouldBindUri(obj) } // Redirect 重定向 func (c *context) Redirect(code int, location string) { c.ctx.Redirect(code, location) } func (c *context) Trace() Trace { t, ok := c.ctx.Get(_TraceName) if !ok || t == nil { return nil } return t.(Trace) } func (c *context) setTrace(trace Trace) { c.ctx.Set(_TraceName, trace) } func (c *context) disableTrace() { c.setTrace(nil) } func (c *context) Logger() logger.CustomLogger { getLogger, ok := c.ctx.Get(_LoggerName) if !ok { return nil } return getLogger.(logger.CustomLogger) } func (c *context) setLogger(logger logger.CustomLogger) { c.ctx.Set(_LoggerName, logger) } func (c *context) getPayload() interface{} { if payload, ok := c.ctx.Get(_PayloadName); ok != false { return payload } return nil } func (c *context) Payload(payload interface{}) { c.ctx.Set(_PayloadName, payload) } func (c *context) File(filePath string) { c.ctx.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", path.Base(filePath))) c.ctx.Writer.Header().Add("Content-Type", "application/octet-stream") c.ctx.File(filePath) } func (c *context) HTML(name string, obj interface{}) { c.ctx.HTML(200, name+".html", obj) } func (c *context) String(str string) { c.ctx.String(200, str) } func (c *context) XML(obj interface{}) { c.ctx.XML(200, obj) } func (c *context) ExcelData(fileName string, data []byte) { c.ctx.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.QueryEscape(fileName))) c.ctx.Writer.Header().Add("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") c.ctx.Data(http.StatusOK, "application/vnd.ms-excel", data) } func (c *context) FormFile(name string) (*multipart.FileHeader, error) { return c.ctx.FormFile(name) } func (c *context) SaveUploadedFile(file *multipart.FileHeader, dst string) error { return c.ctx.SaveUploadedFile(file, dst) } func (c *context) Header() http.Header { header := c.ctx.Request.Header clone := make(http.Header, len(header)) for k, v := range header { value := make([]string, len(v)) copy(value, v) clone[k] = value } return clone } func (c *context) GetHeader(key string) string { return c.ctx.GetHeader(key) } func (c *context) SetHeader(key, value string) { c.ctx.Header(key, value) } func (c *context) SessionUserInfo() proposal.SessionUserInfo { val, ok := c.ctx.Get(_SessionUserInfo) if !ok { return proposal.SessionUserInfo{} } return val.(proposal.SessionUserInfo) } func (c *context) setSessionUserInfo(info proposal.SessionUserInfo) { c.ctx.Set(_SessionUserInfo, info) } func (c *context) AbortWithError(err BusinessError) { if err != nil { httpCode := err.HTTPCode() if httpCode == 0 { httpCode = http.StatusInternalServerError } c.ctx.AbortWithStatus(httpCode) c.ctx.Set(_AbortErrorName, err) } } func (c *context) abortError() BusinessError { err, _ := c.ctx.Get(_AbortErrorName) return err.(BusinessError) } func (c *context) Alias() string { path, ok := c.ctx.Get(_Alias) if !ok { return "" } return path.(string) } func (c *context) setAlias(path string) { if path = strings.TrimSpace(path); path != "" { c.ctx.Set(_Alias, path) } } func (c *context) isRecordMetrics() bool { isRecordMetrics, ok := c.ctx.Get(_IsRecordMetrics) if !ok { return false } return isRecordMetrics.(bool) } func (c *context) ableRecordMetrics() { c.ctx.Set(_IsRecordMetrics, true) } func (c *context) disableRecordMetrics() { c.ctx.Set(_IsRecordMetrics, false) } // RequestInputParams 获取所有参数 func (c *context) RequestInputParams() url.Values { _ = c.ctx.Request.ParseForm() return c.ctx.Request.Form } // RequestPostFormParams 获取 PostForm 参数 func (c *context) RequestPostFormParams() url.Values { _ = c.ctx.Request.ParseForm() return c.ctx.Request.PostForm } // RequestPathParams 获取路由参数的值 func (c *context) RequestPathParams(key string) string { return c.ctx.Param(key) } // Request 获取 Request func (c *context) Request() *http.Request { return c.ctx.Request } func (c *context) RawData() []byte { body, ok := c.ctx.Get(_BodyName) if !ok { return nil } return body.([]byte) } // Method 请求的method func (c *context) Method() string { return c.ctx.Request.Method } // Host 请求的host func (c *context) Host() string { return c.ctx.Request.Host } // Path 请求的路径(不附带querystring) func (c *context) Path() string { return c.ctx.Request.URL.Path } // URI unescape后的uri func (c *context) URI() string { uri, _ := url.QueryUnescape(c.ctx.Request.URL.RequestURI()) return uri } // RequestContext (包装 Trace + Logger) 获取请求的 context (当client关闭后,会自动canceled) func (c *context) RequestContext() StdContext { return StdContext{ c.ctx.Request.Context(), c.Trace(), c.Logger(), } } // ResponseWriter 获取 ResponseWriter func (c *context) ResponseWriter() gin.ResponseWriter { return c.ctx.Writer } // Param 获取 URL 参数 func (c *context) Param(key string) string { return c.ctx.Param(key) }