提交kit/restfulx

Signed-off-by: lixxxww <941403820@qq.com>
This commit is contained in:
lixxxww
2024-01-23 12:09:15 +00:00
committed by Gitee
parent 0ff66b9d43
commit 93d5a562a7
4 changed files with 282 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package restfulx
type LogInfo struct {
LogResp bool // 是否记录返回结果
Description string // 请求描述
}
func NewLogInfo(description string) *LogInfo {
return &LogInfo{Description: description, LogResp: false}
}
func (i *LogInfo) WithLogResp(logResp bool) *LogInfo {
i.LogResp = logResp
return i
}

View File

@@ -0,0 +1,16 @@
package restfulx
type Permission struct {
NeedToken bool // 是否需要token
NeedCasbin bool // 是否进行权限 api路径权限验证
}
func (p *Permission) WithNeedToken(needToken bool) *Permission {
p.NeedToken = needToken
return p
}
func (p *Permission) WithNeedCasBin(needCasBin bool) *Permission {
p.NeedCasbin = needCasBin
return p
}

135
kit/restfulx/req_ctx.go Normal file
View File

@@ -0,0 +1,135 @@
package restfulx
import (
"pandax/kit/biz"
"pandax/kit/token"
"time"
"github.com/emicklei/go-restful/v3"
"github.com/go-playground/validator/v10"
)
// 处理函数
type HandlerFunc func(*ReqCtx)
type ReqCtx struct {
Request *restful.Request
Response *restful.Response
// NeedToken bool // 是否需要token
RequiredPermission *Permission // 需要的权限信息默认为nil需要校验token
LoginAccount *token.Claims // 登录账号信息只有校验token后才会有值
LogInfo *LogInfo // 日志相关信息
ReqParam any // 请求参数,主要用于记录日志
ResData any // 响应结果
Err any // 请求错误
Validate *validator.Validate
Timed int64 // 执行时间
NoRes bool // 无需返回结果,即文件下载等
}
func (rc *ReqCtx) Handle(handler HandlerFunc) {
request := rc.Response
defer func() {
var err any
err = recover()
if err != nil {
rc.Err = err
ErrorRes(request, err)
}
// 应用所有请求后置处理器
ApplyHandlerInterceptor(afterHandlers, rc)
}()
biz.IsTrue(rc.Request != nil, "Request == nil")
// 默认为不记录请求参数可在handler回调函数中覆盖赋值
rc.ReqParam = 0
// 默认响应结果为nil可在handler中赋值
rc.ResData = nil
// 调用请求前所有处理器
err := ApplyHandlerInterceptor(beforeHandlers, rc)
if err != nil {
panic(err)
}
begin := time.Now()
handler(rc)
rc.Timed = time.Now().Sub(begin).Milliseconds()
if !rc.NoRes {
SuccessRes(rc.Response, rc.ResData)
}
}
func (rc *ReqCtx) Download(filename string) {
rc.NoRes = true
Download(rc, filename)
}
func NewReqCtx(request *restful.Request, response *restful.Response) *ReqCtx {
return &ReqCtx{
Request: request,
Response: response,
LogInfo: NewLogInfo("默认日志信息"),
Validate: validator.New(),
RequiredPermission: &Permission{NeedToken: true, NeedCasbin: true},
}
}
// 调用该方法设置请求描述,则默认记录日志,并不记录响应结果
func (r *ReqCtx) WithLog(model string) *ReqCtx {
r.LogInfo.Description = model
return r
}
func (r *ReqCtx) NoAppend() *ReqCtx {
r.NoRes = true
return r
}
// 设置请求上下文需要的权限信息
func (r *ReqCtx) WithRequiredPermission(permission *Permission) *ReqCtx {
r.RequiredPermission = permission
return r
}
// 是否需要token
func (r *ReqCtx) WithNeedToken(needToken bool) *ReqCtx {
r.RequiredPermission.NeedToken = needToken
return r
}
// 是否需要Casbin
func (r *ReqCtx) WithNeedCasbin(needCasbin bool) *ReqCtx {
r.RequiredPermission.NeedCasbin = needCasbin
return r
}
// 处理器拦截器函数
type HandlerInterceptorFunc func(*ReqCtx) error
type HandlerInterceptors []HandlerInterceptorFunc
var (
beforeHandlers HandlerInterceptors
afterHandlers HandlerInterceptors
)
// 使用前置处理器函数
func UseBeforeHandlerInterceptor(b HandlerInterceptorFunc) {
beforeHandlers = append(beforeHandlers, b)
}
// 使用后置处理器函数
func UseAfterHandlerInterceptor(b HandlerInterceptorFunc) {
afterHandlers = append(afterHandlers, b)
}
// 应用指定处理器拦截器,如果有一个错误则直接返回错误
func ApplyHandlerInterceptor(his HandlerInterceptors, rc *ReqCtx) any {
for _, handler := range his {
if err := handler(rc); err != nil {
return err
}
}
return nil
}

116
kit/restfulx/restfulx.go Normal file
View File

@@ -0,0 +1,116 @@
package restfulx
import (
"encoding/json"
"net/http"
"pandax/kit/biz"
"pandax/kit/logger"
"pandax/kit/model"
"strconv"
"github.com/emicklei/go-restful/v3"
"github.com/gorilla/schema"
)
// 绑定并校验请求结构体参数 结构体添加 例如: validate:"required" validate:"required,gt=10"
func BindJsonAndValid(rc *ReqCtx, data any) {
if err := rc.Request.ReadEntity(data); err != nil {
panic(any(biz.NewBizErr(err.Error())))
}
if err := rc.Validate.Struct(data); err != nil {
panic(any(biz.NewBizErr("传参格式错误:" + err.Error())))
}
}
// BindQuery 绑定查询字符串到
func BindQuery(rc *ReqCtx, data any) {
if err := rc.Request.ReadEntity(data); err != nil {
panic(any(biz.NewBizErr(err.Error())))
}
}
// PathParamsToAny 获取虽有路径中的参数
func PathParamsToAny(rc *ReqCtx, in any) {
vars := make(map[string]any)
for k, v := range rc.Request.PathParameters() {
vars[k] = v
}
marshal, _ := json.Marshal(vars)
err := json.Unmarshal(marshal, in)
biz.ErrIsNil(err, "error get path value encoding unmarshal")
return
}
// QueryParamsToAny 获取所有Query的参数
func QueryParamsToAny(rc *ReqCtx, in any) {
err := rc.Request.Request.ParseForm()
biz.ErrIsNil(err, "error get ParseForm value encoding unmarshal")
decoder := schema.NewDecoder()
err = decoder.Decode(in, rc.Request.Request.Form)
biz.ErrIsNil(err, "error get path value encoding unmarshal")
return
}
// GetPageQueryParam 获取分页参数
func GetPageQueryParam(rc *ReqCtx) *model.PageParam {
return &model.PageParam{PageNum: QueryInt(rc, "pageNum", 1), PageSize: QueryInt(rc, "pageSize", 10)}
}
// QueryInt 获取查询参数中指定参数值并转为int
func QueryInt(rc *ReqCtx, qm string, defaultInt int) int {
qv := rc.Request.QueryParameter(qm)
if qv == "" {
return defaultInt
}
qvi, err := strconv.Atoi(qv)
biz.ErrIsNil(err, "query param not int")
return qvi
}
// QueryParam QueryParam
func QueryParam(rc *ReqCtx, key string) string {
return rc.Request.QueryParameter(key)
}
// PathParamInt 获取路径参数
func PathParamInt(rc *ReqCtx, pm string) int {
value, _ := strconv.Atoi(rc.Request.PathParameter(pm))
return value
}
func PathParam(rc *ReqCtx, pm string) string {
return rc.Request.PathParameter(pm)
}
// 文件下载
func Download(rc *ReqCtx, filename string) {
rc.Response.Header().Add("success", "true")
rc.Response.Header().Set("Content-Length", "-1")
rc.Response.Header().Set("Content-Disposition", "attachment; filename="+filename)
http.ServeFile(
rc.Response.ResponseWriter,
rc.Request.Request, filename)
}
// 返回统一成功结果
func SuccessRes(response *restful.Response, data any) {
response.WriteEntity(model.Success(data))
}
// 返回失败结果集
func ErrorRes(response *restful.Response, err any) {
switch t := err.(type) {
case *biz.BizError:
response.WriteEntity(model.Error(t))
break
case error:
response.WriteEntity(model.ServerError())
logger.Log.Error(t)
break
case string:
response.WriteEntity(model.ServerError())
logger.Log.Error(t)
break
default:
logger.Log.Error(t)
}
}