| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- package gin
- import (
- "fmt"
- "github.com/gogf/gf/encoding/gjson"
- "github.com/gogf/gf/frame/g"
- "net/http"
- "net/url"
- "strings"
- "github.com/gin-contrib/cors"
- "github.com/gin-gonic/gin"
- "github.com/gogf/gf/encoding/gbase64"
- "github.com/gogf/gf/util/gconv"
- . "github.com/rpcxio/rpcx-gateway"
- )
- const (
- DS_DT_Excel = "DS-DT-Excel" // 数据类型:Excel数据流
- )
- type Server struct {
- addr string
- g *gin.Engine
- }
- type ExcelDataResp struct {
- Filename string `json:"filename,omitempty"`
- Data []byte
- }
- // New returns a server.
- func New(addr string) *Server {
- return &Server{
- addr: addr,
- }
- }
- // NewWithGin returns a server with preconfigured gin.
- func NewWithGin(addr string, g *gin.Engine) *Server {
- return &Server{
- addr: addr,
- g: g,
- }
- }
- // RegisterHandler configures the handler to handle http rpcx invoke.
- // It wraps ServiceHandler into httprouter.Handle.
- func (s *Server) RegisterHandler(base string, handler ServiceHandler) {
- g := s.g
- if g == nil {
- gin.SetMode(gin.ReleaseMode) // 设备为生产模式 add by sunmiao
- g = gin.Default()
- // 自定义日志输出
- g.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
- return fmt.Sprintf("%s [%s] %s%s %s\n",
- param.ClientIP, // 客户端IP
- param.Request.Header.Get("Tenant"), // 租户码
- param.Path, // 请求路径
- "/"+param.Request.Header.Get("X-RPCX-ServicePath")+"/"+param.Request.Header.Get("X-RPCX-ServiceMethod"),
- //param.StatusCode, // 请求状态码
- //param.Latency, // 请求时长
- param.ErrorMessage,
- )
- }))
- // 添加CORS处理 add by sunmiao
- config := cors.DefaultConfig()
- config.AllowAllOrigins = true
- config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type",
- "Authorization", "Tenant", "X-RPCX-SerializeType", "X-RPCX-ServiceMethod", "SrvEnv",
- "X-RPCX-ServicePath", "X-RPCX-Meta"}
- g.Use(cors.New(config))
- }
- h := wrapServiceHandler(handler)
- g.POST(base, h)
- // 只开放POST edit by sunmiao
- //g.GET(base, h)
- //g.PUT(base, h)
- s.g = g
- }
- func wrapServiceHandler(handler ServiceHandler) gin.HandlerFunc {
- return func(ctx *gin.Context) {
- r := ctx.Request
- w := ctx.Writer
- if r.Header.Get(XServicePath) == "" {
- servicePath := ctx.Param("servicePath")
- if strings.HasPrefix(servicePath, "/") {
- servicePath = servicePath[1:]
- }
- r.Header.Set(XServicePath, servicePath)
- }
- servicePath := r.Header.Get(XServicePath)
- messageID := r.Header.Get(XMessageID)
- wh := w.Header()
- if messageID != "" {
- wh.Set(XMessageID, messageID)
- }
- xmeta := ""
- // 传递租户码
- tenant := r.Header.Get("Tenant")
- if tenant != "" {
- //r.Header.Set(XMeta, "tenant="+tenant)
- xmeta = "tenant=" + tenant
- }
- serviceMethod := r.Header.Get(XServiceMethod)
- if serviceMethod != "" {
- if xmeta != "" {
- xmeta = xmeta + "&"
- }
- xmeta = xmeta + "reqMethod=" + servicePath + "." + serviceMethod
- }
- // 传递ClientIP和UserAgent
- x_Meta := r.Header.Get("X-RPCX-Meta")
- if x_Meta != "" && strings.Contains(x_Meta, "need_clint_Info=1") {
- clientIP := ctx.ClientIP()
- userAgent := gbase64.EncodeString(r.UserAgent())
- if xmeta != "" {
- xmeta = xmeta + "&"
- }
- xmeta = xmeta + "clientIP=" + clientIP + "&userAgent=" + userAgent
- }
- // 追加原始 X-RPCX-Meta
- if xmeta != "" {
- if x_Meta != "" {
- xmeta = x_Meta + "&" + xmeta
- }
- } else {
- xmeta = x_Meta
- }
- r.Header.Set(XMeta, xmeta)
- meta, payload, err := handler(r, servicePath)
- for k, v := range meta {
- wh.Set(k, v)
- }
- // todo 添加日志收集
- reqData := g.Map{}
- ctx.ShouldBindJSON(&reqData)
- logInfo := g.Map{"reqHeader": r.Header, "reqData": reqData, "resMeta": meta, "resData": gjson.New(payload)}
- if err != nil {
- logInfo["err"] = err
- g.Log().Error(logInfo)
- } else {
- g.Log().Info(logInfo)
- }
- if err == nil {
- // Excel文件流下载
- if v, ok := meta[XMeta]; ok {
- metadata, err := url.ParseQuery(v)
- if err != nil {
- resp := errorJson(500, err.Error())
- ctx.JSON(http.StatusOK, resp)
- return
- }
- if _, ok := metadata[DS_DT_Excel]; ok {
- excelData := ExcelDataResp{
- Filename: "temp.xlsx",
- }
- err := gconv.Struct(payload, &excelData)
- if err != nil {
- resp := errorJson(500, err.Error())
- ctx.JSON(http.StatusOK, resp)
- return
- }
- wh.Set("content-disposition", "attachment; filename="+excelData.Filename)
- wh.Set("Access-Control-Expose-Headers", "Content-Disposition")
- ctx.Data(http.StatusOK, "application/vnd.ms-excel", excelData.Data)
- return
- }
- }
- // 普通开发环境改为json格式
- env := r.Header.Get("SrvEnv")
- if env == "dev" {
- ctx.Data(http.StatusOK, "application/json; charset=UTF-8", payload)
- } else {
- // 常规数据流
- ctx.Data(http.StatusOK, "application/octet-stream; charset=UTF-8", payload)
- }
- return
- }
- rh := r.Header
- for k, v := range rh {
- if strings.HasPrefix(k, "X-RPCX-") && len(v) > 0 {
- wh.Set(k, v[0])
- }
- }
- //wh.Set(XMessageStatusType, "Error")
- //wh.Set(XErrorMessage, err.Error())
- //ctx.String(http.StatusOK, err.Error())
- resp := errorJson(500, err.Error())
- if err.Error() == "InvalidToken" {
- resp.Code = 401
- }
- ctx.JSON(http.StatusOK, resp)
- }
- }
- func (s *Server) Serve() error {
- return s.g.Run(s.addr)
- }
- // 数据返回通用JSON数据结构
- type JsonResponse struct {
- Code int `json:"code,omitempty"` // 错误码((200:成功, 其他是异常)
- Msg string `json:"msg,omitempty"` // 提示信息
- Data interface{} `json:"data,omitempty"` // 返回数据(业务接口定义具体数据结构)
- }
- func errorJson(code int, message string, data ...interface{}) JsonResponse {
- responseData := interface{}(nil)
- if len(data) > 0 {
- responseData = data[0]
- }
- jsonData := JsonResponse{
- Code: code,
- Msg: message,
- Data: responseData,
- }
- return jsonData
- }
|