refactor: unify all service error codes

This commit is contained in:
2026-08-12 12:13:38 +08:00
parent fa6898aef8
commit 8a158efb86
4 changed files with 121 additions and 53 deletions

View File

@@ -10,6 +10,7 @@ import (
"strings"
"sync"
"git.apinb.com/bsm-sdk/core/errcode"
"github.com/gin-gonic/gin"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
@@ -35,10 +36,9 @@ type dynamicGateway struct {
}
type dynamicRPCResponse struct {
Code int32 `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
Details []json.RawMessage `json:"details,omitempty"`
Code int32 `json:"code"`
Message string `json:"message"`
Data json.RawMessage `json:"data,omitempty"`
}
func newDynamicGateway(grpcAddr string) (*dynamicGateway, error) {
@@ -67,7 +67,7 @@ func (g *dynamicGateway) handle(c *gin.Context) {
serviceShortName := strings.TrimSpace(c.Param("service"))
methodName := strings.TrimSpace(c.Param("method"))
if moduleName == "" || serviceShortName == "" || methodName == "" {
writeDynamicError(c, status.Error(codes.InvalidArgument, "path must be /rpc/{module}/{service}/{method}"))
writeDynamicError(c, errcode.String(errcode.ErrInvalidArgument, "path must be /rpc/{module}/{service}/{method}"))
return
}
serviceName := moduleName + "." + serviceShortName
@@ -77,23 +77,23 @@ func (g *dynamicGateway) handle(c *gin.Context) {
return
}
if descriptor.IsStreamingClient() || descriptor.IsStreamingServer() {
writeDynamicError(c, status.Error(codes.Unimplemented, "streaming RPC methods are not supported"))
writeDynamicError(c, errcode.String(errcode.ErrUnimplemented, "streaming RPC methods are not supported"))
return
}
body, err := io.ReadAll(io.LimitReader(c.Request.Body, maxDynamicRPCBody+1))
if err != nil {
writeDynamicError(c, status.Error(codes.InvalidArgument, "read request body: "+err.Error()))
writeDynamicError(c, errcode.String(errcode.ErrInvalidArgument, "read request body: "+err.Error()))
return
}
if len(body) > maxDynamicRPCBody {
writeDynamicError(c, status.Error(codes.ResourceExhausted, "request body exceeds 4 MiB"))
writeDynamicError(c, errcode.String(errcode.ErrResourceExhausted, "request body exceeds 4 MiB"))
return
}
request := dynamicpb.NewMessage(descriptor.Input())
if err := (protojson.UnmarshalOptions{DiscardUnknown: false}).Unmarshal(body, request); err != nil {
writeDynamicError(c, status.Error(codes.InvalidArgument, "invalid protobuf JSON: "+err.Error()))
writeDynamicError(c, errcode.String(errcode.ErrJsonUnmarshal, err.Error()))
return
}
response := dynamicpb.NewMessage(descriptor.Output())
@@ -106,7 +106,7 @@ func (g *dynamicGateway) handle(c *gin.Context) {
data, err := (protojson.MarshalOptions{UseProtoNames: false}).Marshal(response)
if err != nil {
writeDynamicError(c, status.Error(codes.Internal, "marshal protobuf response: "+err.Error()))
writeDynamicError(c, errcode.String(errcode.ErrJsonMarshal, err.Error()))
return
}
c.JSON(http.StatusOK, dynamicRPCResponse{Code: int32(codes.OK), Message: codes.OK.String(), Data: data})
@@ -123,51 +123,51 @@ func (g *dynamicGateway) resolveMethod(ctx context.Context, serviceName, methodN
stream, err := reflectionv1.NewServerReflectionClient(g.conn).ServerReflectionInfo(ctx)
if err != nil {
return nil, status.Error(codes.Unavailable, "open gRPC reflection stream: "+err.Error())
return nil, errcode.String(errcode.ErrUnavailable, "open gRPC reflection stream: "+err.Error())
}
if err := stream.Send(&reflectionv1.ServerReflectionRequest{
MessageRequest: &reflectionv1.ServerReflectionRequest_FileContainingSymbol{FileContainingSymbol: serviceName},
}); err != nil {
return nil, status.Error(codes.Unavailable, "query gRPC reflection: "+err.Error())
return nil, errcode.String(errcode.ErrUnavailable, "query gRPC reflection: "+err.Error())
}
reflectionResponse, err := stream.Recv()
if err != nil {
return nil, status.Error(codes.Unavailable, "read gRPC reflection response: "+err.Error())
return nil, errcode.String(errcode.ErrUnavailable, "read gRPC reflection response: "+err.Error())
}
fileResponse := reflectionResponse.GetFileDescriptorResponse()
if fileResponse == nil {
if reflectionErr := reflectionResponse.GetErrorResponse(); reflectionErr != nil {
return nil, status.Error(codes.Code(reflectionErr.ErrorCode), reflectionErr.ErrorMessage)
return nil, sdkError(status.Error(codes.Code(reflectionErr.ErrorCode), reflectionErr.ErrorMessage))
}
return nil, status.Error(codes.NotFound, "service descriptor not found")
return nil, errcode.String(errcode.ErrRecordNotFound, "service descriptor not found")
}
set := &descriptorpb.FileDescriptorSet{}
for _, encoded := range fileResponse.FileDescriptorProto {
file := &descriptorpb.FileDescriptorProto{}
if err := proto.Unmarshal(encoded, file); err != nil {
return nil, status.Error(codes.Internal, "decode reflected descriptor: "+err.Error())
return nil, errcode.String(errcode.ErrInternal, "decode reflected descriptor: "+err.Error())
}
set.File = append(set.File, file)
}
files, err := protodesc.NewFiles(set)
if err != nil {
return nil, status.Error(codes.Internal, "build reflected descriptors: "+err.Error())
return nil, errcode.String(errcode.ErrInternal, "build reflected descriptors: "+err.Error())
}
descriptor, err := files.FindDescriptorByName(protoreflect.FullName(serviceName))
if err != nil {
if err == protoregistry.NotFound {
return nil, status.Error(codes.NotFound, "service not found")
return nil, errcode.String(errcode.ErrRecordNotFound, "service not found")
}
return nil, status.Error(codes.Internal, "resolve service descriptor: "+err.Error())
return nil, errcode.String(errcode.ErrInternal, "resolve service descriptor: "+err.Error())
}
service, ok := descriptor.(protoreflect.ServiceDescriptor)
if !ok {
return nil, status.Error(codes.NotFound, "symbol is not a gRPC service")
return nil, errcode.String(errcode.ErrRecordNotFound, "symbol is not a gRPC service")
}
method = service.Methods().ByName(protoreflect.Name(methodName))
if method == nil {
return nil, status.Error(codes.NotFound, "method not found")
return nil, errcode.String(errcode.ErrRecordNotFound, "method not found")
}
g.mu.Lock()
g.cache[cacheKey] = method
@@ -190,20 +190,5 @@ func outgoingMetadata(request *http.Request) context.Context {
}
func writeDynamicError(c *gin.Context, err error) {
grpcStatus := status.Convert(err)
details := make([]json.RawMessage, 0, len(grpcStatus.Details()))
for _, detail := range grpcStatus.Details() {
message, ok := detail.(proto.Message)
if !ok {
continue
}
if encoded, marshalErr := protojson.Marshal(message); marshalErr == nil {
details = append(details, encoded)
}
}
c.JSON(http.StatusOK, dynamicRPCResponse{
Code: int32(grpcStatus.Code()),
Message: grpcStatus.Message(),
Details: details,
})
c.JSON(http.StatusOK, newErrorResponse(sdkError(err)))
}