mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-01-23 10:50:24 +08:00
优化响应中间件,自动识别响应数据格式
This commit is contained in:
parent
50e64e132f
commit
9b89402bf6
@ -104,6 +104,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
## 感谢(排名不分先后)
|
||||
> gf框架 [https://github.com/gogf/gf](https://github.com/gogf/gf)
|
||||
>
|
||||
@ -118,17 +119,11 @@
|
||||
> gopay [https://github.com/go-pay/gopay](https://github.com/go-pay/gopay)
|
||||
|
||||
|
||||
|
||||
## 交流QQ群
|
||||
交流群①:190966648 <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=mJafkvme3VNyiQlCFIFNRtY8Xlr7pj9U&jump_from=webapi&authKey=jL10vIESr+vO8wpxwyd6DlChzkrbHpzN9uhAsIHgAinL/Vvd+nvuRyilf2UqUlCy"><img border="0" src="https://bufanyun.cn-bj.ufileos.com/hotgo/group.png" alt="HotGo框架交流1群" title="HotGo框架交流1群"></a>
|
||||
> <img src="https://bufanyun.cn-bj.ufileos.com/hotgo/hotgo1qun.png" width="400px"/>
|
||||
|
||||
|
||||
> 感谢你使用HotGo,公司团队精力时间有限,因此我们不再提供免费的技术服务!
|
||||
>
|
||||
> 同时您也可以联系我们,雇佣我们团队为您干活,谢谢合作!
|
||||
|
||||
|
||||
## 商用说明
|
||||
|
||||
> HotGo 是开源免费的,遵循 MIT 开源协议,意味着您无需支付任何费用,也无需授权,即可将它应用到您的产品中。
|
||||
|
@ -67,8 +67,8 @@
|
||||
| --- xxx插件 | 插件模块名称 |
|
||||
| --- --- api | 对外接口。提供服务的输入/输出数据结构定义 |
|
||||
| --- --- --- admin | 后台接口 |
|
||||
| --- --- --- api | 前台通用接口,包含PC页面、uinapp接口等 |
|
||||
| --- --- --- home | 前台PC端页面 |
|
||||
| --- --- --- api | 前台通用接口,包含PC端、移动端接口等 |
|
||||
| --- --- --- home | 前台PC端、H5端页面 |
|
||||
| --- --- --- websocket | 可同时为多应用提供websocket接口 |
|
||||
| --- --- controller | 接收/解析用户输入参数的入口/接口层,也可以理解为控制器 |
|
||||
| --- --- crons | 项目中由系统统一接管的定时任务处理 |
|
||||
@ -83,8 +83,8 @@
|
||||
| --- main.go | 插件始化文件和模块插拔接口 |
|
||||
| **api** | 对外接口。提供服务的输入/输出数据结构定义 |
|
||||
| --- admin | 后台接口 |
|
||||
| --- api | 前台通用接口,包含PC页面、uinapp接口等 |
|
||||
| --- home | 前台PC端页面 |
|
||||
| --- api | 前台通用接口,包含PC端、移动端接口等 |
|
||||
| --- home | 前台PC端、H5端页面 |
|
||||
| --- websocket | 可同时为多应用提供websocket接口 |
|
||||
| **hack** | 存放项目开发工具、脚本等内容例如,CLI工具的配置,各种shell/bat脚本等文件 |
|
||||
| **internal** | 业务逻辑存放目录通过Golang internal特性对外部隐藏可见性 |
|
||||
|
@ -10,7 +10,10 @@ type NotifyAliPayReq struct {
|
||||
g.Meta `path:"/pay/notify/alipay" method:"post" tags:"支付异步通知" summary:"支付宝回调"`
|
||||
}
|
||||
|
||||
type NotifyAliPayRes payin.PayNotifyModel
|
||||
type NotifyAliPayRes struct {
|
||||
g.Meta `mime:"text/html" type:"string" example:"<html/>"`
|
||||
payin.PayNotifyModel
|
||||
}
|
||||
|
||||
// NotifyWxPayReq 微信支付回调
|
||||
type NotifyWxPayReq struct {
|
||||
@ -24,4 +27,7 @@ type NotifyQQPayReq struct {
|
||||
g.Meta `path:"/pay/notify/qqpay" method:"post" tags:"支付异步通知" summary:"QQ支付回调"`
|
||||
}
|
||||
|
||||
type NotifyQQPayRes payin.PayNotifyModel
|
||||
type NotifyQQPayRes struct {
|
||||
g.Meta `mime:"text/xml" type:"string" example:"<html/>"`
|
||||
payin.PayNotifyModel
|
||||
}
|
||||
|
@ -11,8 +11,9 @@ import (
|
||||
|
||||
type HelloReq struct {
|
||||
g.Meta `path:"/hello" tags:"Hello" method:"get" summary:"You first hello api"`
|
||||
Name string `json:"name" d:"hotgo" dc:"名字"`
|
||||
}
|
||||
|
||||
type HelloRes struct {
|
||||
g.Meta `mime:"text/html" example:"string"`
|
||||
Tips string `json:"tips"`
|
||||
}
|
||||
|
8
server/internal/consts/http.go
Normal file
8
server/internal/consts/http.go
Normal file
@ -0,0 +1,8 @@
|
||||
package consts
|
||||
|
||||
const (
|
||||
HTTPContentTypeXml = "text/xml"
|
||||
HTTPContentTypeHtml = "text/html"
|
||||
HTTPContentTypeStream = "text/event-stream"
|
||||
HTTPContentTypeJson = "application/json"
|
||||
)
|
@ -7,8 +7,10 @@ package pay
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"hotgo/api/api/pay"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/internal/model/input/payin"
|
||||
"hotgo/internal/service"
|
||||
)
|
||||
@ -21,32 +23,35 @@ type cNotify struct{}
|
||||
|
||||
// AliPay 支付宝回调
|
||||
func (c *cNotify) AliPay(ctx context.Context, _ *pay.NotifyAliPayReq) (res *pay.NotifyAliPayRes, err error) {
|
||||
_, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeAliPay})
|
||||
if err != nil {
|
||||
if _, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeAliPay}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res = &pay.NotifyAliPayRes{PayType: consts.PayTypeAliPay, Message: "success"}
|
||||
|
||||
response.RText(g.RequestFromCtx(ctx), "success")
|
||||
return
|
||||
}
|
||||
|
||||
// WxPay 微信支付回调
|
||||
func (c *cNotify) WxPay(ctx context.Context, _ *pay.NotifyWxPayReq) (res *pay.NotifyWxPayRes, err error) {
|
||||
_, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeWxPay})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if _, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeWxPay}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
res = &pay.NotifyWxPayRes{PayType: consts.PayTypeWxPay, Code: "SUCCESS", Message: "收单成功"}
|
||||
response.CustomJson(g.RequestFromCtx(ctx), `{"code": "SUCCESS","message": "收单成功"}`)
|
||||
return
|
||||
}
|
||||
|
||||
// QQPay QQ支付回调
|
||||
func (c *cNotify) QQPay(ctx context.Context, _ *pay.NotifyQQPayReq) (res *pay.NotifyQQPayRes, err error) {
|
||||
_, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeQQPay})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res = &pay.NotifyQQPayRes{PayType: consts.PayTypeQQPay, Message: "SUCCESS"}
|
||||
if _, err = service.Pay().Notify(ctx, payin.PayNotifyInp{PayType: consts.PayTypeQQPay}); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r := g.RequestFromCtx(ctx)
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.Write(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||
r.Response.WriteXml(g.Map{
|
||||
"return_code": "SUCCESS",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -7,8 +7,9 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"fmt"
|
||||
"hotgo/api/api/user"
|
||||
"hotgo/utility/simple"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -17,7 +18,9 @@ var (
|
||||
|
||||
type cHello struct{}
|
||||
|
||||
func (c *cHello) Hello(ctx context.Context, _ *user.HelloReq) (res *user.HelloRes, err error) {
|
||||
g.RequestFromCtx(ctx).Response.Writeln("Hello World api member!")
|
||||
func (c *cHello) Hello(ctx context.Context, req *user.HelloReq) (res *user.HelloRes, err error) {
|
||||
res = &user.HelloRes{
|
||||
Tips: fmt.Sprintf("hello %v, this is the api for %v applications.", req.Name, simple.AppName(ctx)),
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/model"
|
||||
"time"
|
||||
@ -50,22 +51,23 @@ func RJson(r *ghttp.Request, code int, message string, data ...interface{}) {
|
||||
contexts.SetResponse(r.Context(), res)
|
||||
}
|
||||
|
||||
// SusJson 返回成功JSON
|
||||
func SusJson(isExit bool, r *ghttp.Request, message string, data ...interface{}) {
|
||||
if isExit {
|
||||
JsonExit(r, gcode.CodeOK.Code(), message, data...)
|
||||
return
|
||||
}
|
||||
RJson(r, gcode.CodeOK.Code(), message, data...)
|
||||
}
|
||||
// CustomJson 自定义JSON
|
||||
func CustomJson(r *ghttp.Request, content interface{}) {
|
||||
// 清空响应
|
||||
r.Response.ClearBuffer()
|
||||
|
||||
// FailJson 返回失败JSON
|
||||
func FailJson(isExit bool, r *ghttp.Request, message string, data ...interface{}) {
|
||||
if isExit {
|
||||
JsonExit(r, gcode.CodeNil.Code(), message, data...)
|
||||
return
|
||||
}
|
||||
RJson(r, gcode.CodeNil.Code(), message, data...)
|
||||
// 写入响应
|
||||
r.Response.WriteJson(content)
|
||||
|
||||
// 加入到上下文
|
||||
contexts.SetResponse(r.Context(), &model.Response{
|
||||
Code: 0,
|
||||
Message: "",
|
||||
Data: content,
|
||||
Error: nil,
|
||||
Timestamp: gtime.Timestamp(),
|
||||
TraceID: gctx.CtxId(r.Context()),
|
||||
})
|
||||
}
|
||||
|
||||
// Redirect 重定向
|
||||
@ -73,11 +75,6 @@ func Redirect(r *ghttp.Request, location string, code ...int) {
|
||||
r.Response.RedirectTo(location, code...)
|
||||
}
|
||||
|
||||
// Download 下载文件
|
||||
func Download(r *ghttp.Request, location string, code ...int) {
|
||||
r.Response.ServeFileDownload("test.txt")
|
||||
}
|
||||
|
||||
// RText 返回成功文本
|
||||
func RText(r *ghttp.Request, message string) {
|
||||
// 清空响应
|
||||
|
@ -6,8 +6,6 @@ import (
|
||||
|
||||
// HomeAuth 前台页面鉴权中间件
|
||||
func (s *sMiddleware) HomeAuth(r *ghttp.Request) {
|
||||
r.Response.Header().Set("Content-Type", "text/html")
|
||||
|
||||
// 鉴权
|
||||
// ...
|
||||
|
||||
|
@ -32,7 +32,6 @@ import (
|
||||
type sMiddleware struct {
|
||||
LoginUrl string // 登录路由地址
|
||||
DemoWhiteList g.Map // 演示模式放行的路由白名单
|
||||
PayNotifyRoutes g.Map // 支付异步通知路由
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -47,11 +46,6 @@ func NewMiddleware() *sMiddleware {
|
||||
"/admin/site/mobileLogin": struct{}{}, // 手机号登录
|
||||
"/admin/genCodes/preview": struct{}{}, // 预览代码
|
||||
},
|
||||
PayNotifyRoutes: g.Map{
|
||||
"/api/pay/notify/alipay": struct{}{}, // 支付宝
|
||||
"/api/pay/notify/wxpay": struct{}{}, // 微信支付
|
||||
"/api/pay/notify/qqpay": struct{}{}, // QQ支付
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,17 +19,17 @@ func (s *sMiddleware) Develop(r *ghttp.Request) {
|
||||
}
|
||||
|
||||
if !gstr.InArray(ips, "*") {
|
||||
cuIp := location.GetClientIp(r)
|
||||
clientIp := location.GetClientIp(r)
|
||||
ok := false
|
||||
for _, ip := range ips {
|
||||
if ip == cuIp {
|
||||
if ip == clientIp {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !ok {
|
||||
response.JsonExit(r, gcode.CodeNotSupported.Code(), fmt.Sprintf("当前IP[%s]没有配置生成白名单!", cuIp))
|
||||
response.JsonExit(r, gcode.CodeNotSupported.Code(), fmt.Sprintf("当前IP[%s]没有配置生成白名单!", clientIp))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -6,45 +6,41 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"hotgo/internal/consts"
|
||||
"hotgo/internal/library/contexts"
|
||||
"hotgo/internal/library/response"
|
||||
"hotgo/internal/model/input/payin"
|
||||
"hotgo/utility/charset"
|
||||
"hotgo/utility/simple"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ResponseHandler HTTP响应预处理
|
||||
func (s *sMiddleware) ResponseHandler(r *ghttp.Request) {
|
||||
r.Middleware.Next()
|
||||
|
||||
contentType := getContentType(r)
|
||||
// 已存在响应
|
||||
if r.Response.BufferLength() > 0 && contexts.Get(r.Context()).Response != nil {
|
||||
if contentType != consts.HTTPContentTypeStream && r.Response.BufferLength() > 0 && contexts.Get(r.Context()).Response != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// html模板响应
|
||||
if r.Response.Header().Get("Content-Type") == "text/html" {
|
||||
switch contentType {
|
||||
case consts.HTTPContentTypeHtml:
|
||||
s.responseHtml(r)
|
||||
return
|
||||
}
|
||||
|
||||
// 支付通知响应
|
||||
if _, ok := s.PayNotifyRoutes[r.Router.Uri]; ok {
|
||||
s.responsePayNotify(r)
|
||||
case consts.HTTPContentTypeXml:
|
||||
s.responseXml(r)
|
||||
return
|
||||
}
|
||||
|
||||
// 默认json响应
|
||||
case consts.HTTPContentTypeStream:
|
||||
// ...
|
||||
default:
|
||||
responseJson(r)
|
||||
}
|
||||
}
|
||||
|
||||
// responseHtml html模板响应
|
||||
func (s *sMiddleware) responseHtml(r *ghttp.Request) {
|
||||
@ -54,56 +50,23 @@ func (s *sMiddleware) responseHtml(r *ghttp.Request) {
|
||||
}
|
||||
|
||||
r.Response.ClearBuffer()
|
||||
_ = r.Response.WriteTplContent(simple.DefaultErrorTplContent(r.Context()), g.Map{
|
||||
"code": code,
|
||||
"message": message,
|
||||
"stack": resp,
|
||||
})
|
||||
_ = r.Response.WriteTplContent(simple.DefaultErrorTplContent(r.Context()), g.Map{"code": code, "message": message, "stack": resp})
|
||||
return
|
||||
}
|
||||
|
||||
// responsePayNotify 支付通知响应
|
||||
func (s *sMiddleware) responsePayNotify(r *ghttp.Request) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
err error
|
||||
data *payin.PayNotifyModel
|
||||
)
|
||||
|
||||
// responseXml xml响应
|
||||
func (s *sMiddleware) responseXml(r *ghttp.Request) {
|
||||
code, message, resp := parseResponse(r)
|
||||
if code != gcode.CodeOK.Code() {
|
||||
response.RJson(r, code, message, data)
|
||||
return
|
||||
}
|
||||
|
||||
if err = gconv.Scan(resp, &data); err != nil || data == nil {
|
||||
g.Log("exception").Errorf(ctx, "middleware.responsePayNotify Scan err:%+v, data:%+v", err, data)
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.WriteStatus(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
switch data.PayType {
|
||||
case consts.PayTypeAliPay:
|
||||
response.RText(r, data.Message)
|
||||
|
||||
case consts.PayTypeWxPay:
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.WriteJson(fmt.Sprintf(`{"code": "%v","message": "%v"}`, data.Code, data.Message))
|
||||
|
||||
case consts.PayTypeQQPay:
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.Write(`<?xml version="1.0" encoding="UTF-8"?>`)
|
||||
r.Response.WriteXml(g.Map{
|
||||
"return_code": data.Message,
|
||||
})
|
||||
|
||||
switch code {
|
||||
case gcode.CodeOK.Code():
|
||||
r.Response.WriteXml(g.Map{"code": code, "message": message, "data": resp})
|
||||
default:
|
||||
err = gerror.Newf("无效的支付方式,这可能是没有配置通知回调响应方式导致的:%+v", data)
|
||||
g.Log("exception").Error(ctx, err)
|
||||
r.Response.ClearBuffer()
|
||||
r.Response.WriteStatus(http.StatusInternalServerError, err.Error())
|
||||
r.Response.WriteXml(g.Map{"code": code, "message": message, "error": resp})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// responseJson json响应
|
||||
@ -114,11 +77,8 @@ func responseJson(r *ghttp.Request) {
|
||||
|
||||
// parseResponse 解析响应数据
|
||||
func parseResponse(r *ghttp.Request) (code int, message string, resp interface{}) {
|
||||
var (
|
||||
ctx = r.Context()
|
||||
err = r.GetError()
|
||||
)
|
||||
|
||||
ctx := r.Context()
|
||||
err := r.GetError()
|
||||
if err == nil {
|
||||
return gcode.CodeOK.Code(), "操作成功", r.GetHandlerResponse()
|
||||
}
|
||||
@ -126,12 +86,15 @@ func parseResponse(r *ghttp.Request) (code int, message string, resp interface{}
|
||||
// 是否输出错误堆栈到页面
|
||||
if g.Cfg().MustGet(ctx, "hotgo.debug", true).Bool() {
|
||||
message = gerror.Current(err).Error()
|
||||
if getContentType(r) == consts.HTTPContentTypeHtml {
|
||||
resp = charset.SerializeStack(err)
|
||||
} else {
|
||||
resp = charset.ParseErrStack(err)
|
||||
}
|
||||
} else {
|
||||
message = consts.ErrorMessage(gerror.Current(err))
|
||||
}
|
||||
|
||||
// 解析错误状态码
|
||||
code = gerror.Code(err).Code()
|
||||
|
||||
// 记录异常日志
|
||||
@ -142,3 +105,18 @@ func parseResponse(r *ghttp.Request) (code int, message string, resp interface{}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getContentType(r *ghttp.Request) (contentType string) {
|
||||
contentType = r.Response.Header().Get("Content-Type")
|
||||
if contentType != "" {
|
||||
return
|
||||
}
|
||||
|
||||
mime := gmeta.Get(r.GetHandlerResponse(), "mime").String()
|
||||
if mime == "" {
|
||||
contentType = consts.HTTPContentTypeJson
|
||||
} else {
|
||||
contentType = mime
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -16,10 +16,9 @@ import (
|
||||
|
||||
// Home 前台页面路由
|
||||
func Home(ctx context.Context, group *ghttp.RouterGroup) {
|
||||
// 允许通过根地址访问的路由可以同时加到这里
|
||||
// 访问地址:http://127.0.0.1:8000
|
||||
group.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.Middleware(service.Middleware().HomeAuth)
|
||||
// 允许通过根地址访问的路由可以加到这里,访问地址:http://127.0.0.1:8000
|
||||
group.Bind(
|
||||
base.Site, // 基础
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user