refactor: nstatus重构,重新定义错误处理。

main v1.2.0
NoahLan 11 months ago
parent ea15293e22
commit 5f4db21c11

@ -2,11 +2,13 @@ package nstatus
import "git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
// NewApiErr 构建Api错误code为http状态码
func NewApiErr(code int, message string) error {
return NewResult(code, message)
return NewResult(code, message, ApiErr)
}
func NewApiErrWithCode(code int) error {
// NewApiErrNoMsg 构建Api错误空错误消息
func NewApiErrNoMsg(code int) error {
return NewApiErr(code, "")
}

@ -0,0 +1,11 @@
package nstatus
// NewBizErr 构建业务错误
func NewBizErr(code int, message string) error {
return NewResult(code, message, BizErr)
}
// NewBizErrNoMsg 构建业务错误,空错误消息
func NewBizErrNoMsg(code int) error {
return NewBizErr(code, "")
}

@ -1,30 +1,31 @@
package nstatus
import (
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var table = map[codes.Code]int{
codes.OK: code.StatusOK,
codes.Canceled: code.StatusRequestTimeout,
codes.Unknown: code.StatusInternalServerError,
codes.InvalidArgument: code.StatusBadRequest,
codes.DeadlineExceeded: code.StatusGatewayTimeout,
codes.NotFound: code.StatusNotFound,
codes.AlreadyExists: code.StatusConflict,
codes.PermissionDenied: code.StatusForbidden,
codes.ResourceExhausted: code.StatusTooManyRequests,
codes.FailedPrecondition: code.StatusBadRequest,
codes.Aborted: code.StatusConflict,
codes.OutOfRange: code.StatusBadRequest,
codes.Unimplemented: code.StatusNotImplemented,
codes.Internal: code.StatusInternalServerError,
codes.Unavailable: code.StatusServiceUnavailable,
codes.DataLoss: code.StatusInternalServerError,
codes.Unauthenticated: code.StatusUnauthorized,
}
//var table = map[codes.Code]int{
// codes.OK: code.StatusOK,
// codes.Canceled: code.StatusRequestTimeout,
// codes.Unknown: code.StatusInternalServerError,
// codes.InvalidArgument: code.StatusBadRequest,
// codes.DeadlineExceeded: code.StatusGatewayTimeout,
// codes.NotFound: code.StatusNotFound,
// codes.AlreadyExists: code.StatusConflict,
// codes.PermissionDenied: code.StatusForbidden,
// codes.ResourceExhausted: code.StatusTooManyRequests,
// codes.FailedPrecondition: code.StatusBadRequest,
// codes.Aborted: code.StatusConflict,
// codes.OutOfRange: code.StatusBadRequest,
// codes.Unimplemented: code.StatusNotImplemented,
// codes.Internal: code.StatusInternalServerError,
// codes.Unavailable: code.StatusServiceUnavailable,
// codes.DataLoss: code.StatusInternalServerError,
// codes.Unauthenticated: code.StatusUnauthorized,
//}
const GrpcResultTypeKey = "_RESULT_TYPE"
// WrapGrpcErr 将grpc-error转换为 Result
func WrapGrpcErr(err error) *Result {
@ -32,10 +33,10 @@ func WrapGrpcErr(err error) *Result {
if gErr == nil {
return nil
}
if c, ok := table[gErr.Code()]; ok {
return NewResult(c, gErr.Message())
}
return NewResult(int(gErr.Code()), gErr.Message())
//if c, ok := table[gErr.Code()]; ok {
// return NewResult(c, gErr.Message(), RpcErr)
//}
return NewResult(int(gErr.Code()), gErr.Message(), RpcErr)
}
// IsGrpcErr return an error if grpc status
@ -52,7 +53,7 @@ func IsGrpcErr(err error) bool {
}
func NewGrpcErr(code codes.Code, msg string) error {
return NewResult(int(code), msg)
return NewResult(int(code), msg, RpcErr)
}
// NewGrpcInternalErr returns status error with Internal error code.

@ -5,19 +5,21 @@ import (
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// UnaryServerInterceptor grpc 错误拦截 服务端拦截器
// 将服务端传递出的任何错误拦截并转换为标准grpc-status保持code与msg不进行翻译
// 将Result中的Type加入到metadata中进行传递
func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
resp, err = handler(ctx, req)
if err != nil {
cause := errors.Cause(err)
e := ConvertErr(cause)
if e != nil {
writeResultTypeCtx(ctx, e)
// 转换为grpc-err
err = status.Error(codes.Code(e.Code), e.Msg)
}
@ -26,6 +28,10 @@ func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
}
}
func writeResultTypeCtx(ctx context.Context, r *Result) {
_ = grpc.SetHeader(ctx, metadata.Pairs(GrpcResultTypeKey, string(r.Type)))
}
// UnaryClientInterceptor 将客户端调用后产生的grpc-err转换为 *Result
// 客户端直接强转为*Result 或 再次调用 ConvertErr 即可使用
func UnaryClientInterceptor() grpc.UnaryClientInterceptor {
@ -34,6 +40,15 @@ func UnaryClientInterceptor() grpc.UnaryClientInterceptor {
if err == nil {
return nil
}
return ConvertErr(err)
e := ConvertErr(err)
// 读取header(metadata)中的GrpcResultType
md, ok := metadata.FromOutgoingContext(ctx)
if ok {
rt := md.Get(GrpcResultTypeKey)
if len(rt) > 0 {
e.Type = ResultType(rt[0])
}
}
return e
}
}

@ -1,45 +1,79 @@
package nstatus
import (
"encoding/json"
"fmt"
"fmt"
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
"git.noahlan.cn/noahlan/ntool/njson"
)
type ResultType string
const (
CommonResult ResultType = "common" // 一般返回值
ApiErr = "api_err" // ApiErr Api错误用于返回携带http状态码的错误返回信息
BizErr = "biz_err" // BizErr 业务错误错误状态码统一为500详细状态码在返回体的code中
RpcErr = "rpc_err" // RpcErr Rpc错误
)
// Result 统一返回结果 结构体
// {"code":200,"msg":"OK","data":{}}
// API RPC 共用
type Result struct {
Code int `json:"code"` // 返回编码(业务+错误)
Msg string `json:"msg"` // 消息
Data any `json:"data,omitempty,optional"` // 数据
Code int `json:"code"` // 返回编码(业务+错误)
Msg string `json:"msg"` // 消息
Data any `json:"data,omitempty,optional"` // 数据
Type ResultType `json:"-,omitempty,optional"` // Typ 返回结果类型忽略json
}
func NewResult(code int, message string) *Result {
return NewResultWithData(code, message, nil)
func NewResult(code int, message string, typ ResultType) *Result {
return NewResultWithData(code, message, typ, nil)
}
func NewResultWithData(code int, message string, data any) *Result {
return &Result{
Code: code,
Msg: message,
Data: data,
}
func NewResultWithData(code int, message string, typ ResultType, data any) *Result {
return &Result{
Code: code,
Msg: message,
Data: data,
Type: typ,
}
}
// Error 错误输出,同时可作为错误
func (r *Result) Error() string {
return r.String()
return r.Msg
}
func (r *Result) Err() error {
return r
}
func (r *Result) Cause() error {
return r
}
func (r *Result) String() string {
return fmt.Sprintf("Code:%d Message:%s Data:%v", r.Code, r.Msg, r.Data)
return fmt.Sprintf("Code:%d Message:%s Data:%v", r.Code, r.Msg, r.Data)
}
// JsonString 返回JsonString格式
func (r *Result) JsonString() string {
bytes, err := json.Marshal(r)
if err != nil {
return ""
}
return string(bytes)
return njson.MarshalStrSafe(r)
}
// FromError returns a Status representation of err.
//
// - If err was produced by this package *Result`, the appropriate Result is returned.
// - If err is nil, a nil is returned.
//
// - Otherwise, err is an error not compatible with this package. In this
// case, a *Result is returned with code.StatusUnknown and err's Error() message,
// and ok is false.
func FromError(err error) (*Result, bool) {
if err == nil {
return nil, false
}
if se, ok := err.(*Result); ok {
return se, true
}
return NewResult(code.StatusUnknown, err.Error(), CommonResult), false
}

@ -1,34 +0,0 @@
package nstatus
import (
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
)
// ConvertErr is a convenience function which removes the need to handle the
// boolean return value from FromError.
func ConvertErr(err error) *Result {
s, ok := FromError(err)
if !ok {
// 尝试grpc
return WrapGrpcErr(err)
}
return s
}
// FromError returns a Status representation of err.
//
// - If err was produced by this package *Result`, the appropriate Result is returned.
// - If err is nil, a nil is returned.
//
// - Otherwise, err is an error not compatible with this package. In this
// case, a *Result is returned with code.StatusUnknown and err's Error() message,
// and ok is false.
func FromError(err error) (s *Result, ok bool) {
if err == nil {
return nil, false
}
if se, ok := err.(*Result); ok {
return se, true
}
return NewResult(code.StatusUnknown, err.Error()), false
}

@ -0,0 +1,12 @@
package nstatus
// ConvertErr is a convenience function which removes the need to handle the
// boolean return value from FromError.
func ConvertErr(err error) *Result {
s, ok := FromError(err)
if !ok {
// 尝试grpc
return WrapGrpcErr(err)
}
return s
}

@ -3,7 +3,6 @@ package statusz
import (
"git.noahlan.cn/noahlan/ntool-biz/core/i18n"
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus"
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/code"
"git.noahlan.cn/noahlan/ntool-biz/core/nstatus/msg"
"github.com/zeromicro/go-zero/rest/httpx"
"net/http"
@ -12,27 +11,31 @@ import (
// ResponseHandler API执行结果处理统一返回值类型与结构
func ResponseHandler(req *http.Request, w http.ResponseWriter, trans bool, resp any, err error) {
if err == nil {
r := nstatus.NewResultWithData(code.StatusOK, msg.Success, resp)
r := nstatus.NewResultWithData(http.StatusOK, msg.Success, nstatus.CommonResult, resp)
if trans {
r.Msg = i18n.Trans(req.Context(), r.Msg)
}
httpx.WriteJson(w, code.StatusOK, r)
httpx.WriteJson(w, http.StatusOK, r)
return
}
result := nstatus.ConvertErr(err)
if result == nil {
// 不可能发生此情况,这里处理是以防万一
result = nstatus.NewResult(code.StatusFailed, msg.Failed)
result = nstatus.NewResult(http.StatusOK, msg.Success, nstatus.CommonResult)
}
result.Data = resp
if trans {
result.Msg = i18n.Trans(req.Context(), result.Msg)
}
c := result.Code
// 判断code如果不是http-status全部使用500
if result.Code > http.StatusNetworkAuthenticationRequired {
c = http.StatusInternalServerError
c := http.StatusInternalServerError
if result.Type == nstatus.ApiErr {
// API错误按照Result内的code返回若code不是标准http错误则按照500返回
c = result.Code
if c > http.StatusNetworkAuthenticationRequired {
c = http.StatusInternalServerError
}
}
// 其它错误统一按照500返回
httpx.WriteJson(w, c, result)
}

Loading…
Cancel
Save