1
0
mirror of https://github.com/go-kratos/kratos.git synced 2025-11-06 08:59:18 +02:00

errors: refactor status code (#948)

* refactor status code
This commit is contained in:
Tony Chen
2021-05-25 15:01:53 +08:00
committed by GitHub
parent cc0221b5ce
commit 66412031fd
11 changed files with 94 additions and 121 deletions

View File

@@ -11,22 +11,24 @@ import (
"google.golang.org/protobuf/proto"
)
// UnknownReason is unknown reason for error info.
const UnknownReason = ""
//go:generate protoc -I. --go_out=paths=source_relative:. errors.proto
func (e *Error) Error() string {
return fmt.Sprintf("error: domain = %s reason = %s metadata = %v", e.Domain, e.Reason, e.Metadata)
return fmt.Sprintf("error: code = %d reason = %s message = %s metadata = %v", e.Code, e.Reason, e.Message, e.Metadata)
}
// HTTPStatus return an HTTP error code.
func (e *Error) HTTPStatus() int {
return httputil.StatusFromGRPCCode(codes.Code(e.Code))
// StatusCode return an HTTP error code.
func (e *Error) StatusCode() int {
return int(e.Code)
}
// GRPCStatus returns the Status represented by se.
func (e *Error) GRPCStatus() *status.Status {
s, _ := status.New(codes.Code(e.Code), e.Message).
s, _ := status.New(httputil.GRPCCodeFromStatus(e.StatusCode()), e.Message).
WithDetails(&errdetails.ErrorInfo{
Domain: e.Domain,
Reason: e.Reason,
Metadata: e.Metadata,
})
@@ -36,7 +38,7 @@ func (e *Error) GRPCStatus() *status.Status {
// Is matches each error in the chain with the target value.
func (e *Error) Is(err error) bool {
if se := new(Error); errors.As(err, &se) {
return se.Domain == e.Domain && se.Reason == e.Reason
return se.Reason == e.Reason
}
return false
}
@@ -49,23 +51,22 @@ func (e *Error) WithMetadata(md map[string]string) *Error {
}
// New returns an error object for the code, message.
func New(code codes.Code, domain, reason, message string) *Error {
func New(code int, reason, message string) *Error {
return &Error{
Code: int32(code),
Message: message,
Domain: domain,
Reason: reason,
}
}
// Newf New(code fmt.Sprintf(format, a...))
func Newf(code codes.Code, domain, reason, format string, a ...interface{}) *Error {
return New(code, domain, reason, fmt.Sprintf(format, a...))
func Newf(code int, reason, format string, a ...interface{}) *Error {
return New(code, reason, fmt.Sprintf(format, a...))
}
// Errorf returns an error object for the code, message and error info.
func Errorf(code codes.Code, domain, reason, format string, a ...interface{}) error {
return New(code, domain, reason, fmt.Sprintf(format, a...))
func Errorf(code int, reason, format string, a ...interface{}) error {
return New(code, reason, fmt.Sprintf(format, a...))
}
// Code returns the code for a particular error.
@@ -80,22 +81,13 @@ func Code(err error) codes.Code {
return codes.Unknown
}
// Domain returns the domain for a particular error.
// It supports wrapped errors.
func Domain(err error) string {
if se := FromError(err); err != nil {
return se.Domain
}
return ""
}
// Reason returns the reason for a particular error.
// It supports wrapped errors.
func Reason(err error) string {
if se := FromError(err); err != nil {
return se.Reason
}
return ""
return UnknownReason
}
// FromError try to convert an error to *Error.
@@ -113,13 +105,12 @@ func FromError(err error) *Error {
switch d := detail.(type) {
case *errdetails.ErrorInfo:
return New(
gs.Code(),
d.Domain,
httputil.StatusFromGRPCCode(gs.Code()),
d.Reason,
gs.Message(),
).WithMetadata(d.Metadata)
}
}
}
return New(gs.Code(), "", "", err.Error())
return New(httputil.StatusFromGRPCCode(gs.Code()), UnknownReason, err.Error())
}

View File

@@ -25,12 +25,10 @@ type Error struct {
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
// error details
Reason string `protobuf:"bytes,3,opt,name=reason,proto3" json:"reason,omitempty"`
Domain string `protobuf:"bytes,4,opt,name=domain,proto3" json:"domain,omitempty"`
Metadata map[string]string `protobuf:"bytes,5,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
}
func (x *Error) Reset() {
@@ -72,13 +70,6 @@ func (x *Error) GetCode() int32 {
return 0
}
func (x *Error) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Error) GetReason() string {
if x != nil {
return x.Reason
@@ -86,9 +77,9 @@ func (x *Error) GetReason() string {
return ""
}
func (x *Error) GetDomain() string {
func (x *Error) GetMessage() string {
if x != nil {
return x.Domain
return x.Message
}
return ""
}
@@ -104,28 +95,26 @@ var File_errors_proto protoreflect.FileDescriptor
var file_errors_proto_rawDesc = []byte{
0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xe2, 0x01,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0xca, 0x01,
0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18,
0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65,
0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x16, 0x0a,
0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64,
0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x3e, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73,
0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74,
0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a, 0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,
0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
0x38, 0x01, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f, 0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x01,
0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32,
0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02,
0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x33,
0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61,
0x73, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x3e, 0x0a,
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x22, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e,
0x45, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e,
0x74, 0x72, 0x79, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x1a, 0x3b, 0x0a,
0x0d, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x59, 0x0a, 0x18, 0x63, 0x6f,
0x6d, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x6b,
0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x3b,
0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0xa2, 0x02, 0x0c, 0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45,
0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -9,9 +9,7 @@ option objc_class_prefix = "KratosErrors";
message Error {
int32 code = 1;
string message = 2;
// error details
string reason = 3;
string domain = 4;
map<string, string> metadata = 5;
string reason = 2;
string message = 3;
map<string, string> metadata = 4;
};

View File

@@ -3,17 +3,16 @@ package errors
import (
"errors"
"fmt"
"net/http"
"testing"
"google.golang.org/grpc/codes"
)
func TestError(t *testing.T) {
var (
base *Error
)
err := Newf(codes.InvalidArgument, "domain", "reason", "message")
err2 := Newf(codes.InvalidArgument, "domain", "reason", "message")
err := Newf(http.StatusBadRequest, "reason", "message")
err2 := Newf(http.StatusBadRequest, "reason", "message")
err3 := err.WithMetadata(map[string]string{
"foo": "bar",
})
@@ -36,9 +35,6 @@ func TestError(t *testing.T) {
t.Errorf("should be matchs: %v", err)
}
if domain := Domain(err); domain != err2.Domain {
t.Errorf("got %s want: %s", domain, err)
}
if reason := Reason(err); reason != err3.Reason {
t.Errorf("got %s want: %s", reason, err)
}
@@ -49,7 +45,7 @@ func TestError(t *testing.T) {
gs := err.GRPCStatus()
se := FromError(gs.Err())
if se.Domain != err.Domain || se.Reason != se.Reason {
if se.Reason != se.Reason {
t.Errorf("got %+v want %+v", se, err)
}
}

View File

@@ -1,82 +1,78 @@
package errors
import (
"google.golang.org/grpc/codes"
)
// BadRequest new BadRequest error that is mapped to a 400 response.
func BadRequest(domain, reason, message string) *Error {
return Newf(codes.InvalidArgument, domain, reason, message)
func BadRequest(reason, message string) *Error {
return Newf(400, reason, message)
}
// IsBadRequest determines if err is an error which indicates a BadRequest error.
// It supports wrapped errors.
func IsBadRequest(err error) bool {
return Code(err) == codes.InvalidArgument
return Code(err) == 400
}
// Unauthorized new Unauthorized error that is mapped to a 401 response.
func Unauthorized(domain, reason, message string) *Error {
return Newf(codes.Unauthenticated, domain, reason, message)
func Unauthorized(reason, message string) *Error {
return Newf(401, reason, message)
}
// IsUnauthorized determines if err is an error which indicates a Unauthorized error.
// It supports wrapped errors.
func IsUnauthorized(err error) bool {
return Code(err) == codes.Unauthenticated
return Code(err) == 401
}
// Forbidden new Forbidden error that is mapped to a 403 response.
func Forbidden(domain, reason, message string) *Error {
return Newf(codes.PermissionDenied, domain, reason, message)
func Forbidden(reason, message string) *Error {
return Newf(403, reason, message)
}
// IsForbidden determines if err is an error which indicates a Forbidden error.
// It supports wrapped errors.
func IsForbidden(err error) bool {
return Code(err) == codes.PermissionDenied
return Code(err) == 403
}
// NotFound new NotFound error that is mapped to a 404 response.
func NotFound(domain, reason, message string) *Error {
return Newf(codes.NotFound, domain, reason, message)
func NotFound(reason, message string) *Error {
return Newf(404, reason, message)
}
// IsNotFound determines if err is an error which indicates an NotFound error.
// It supports wrapped errors.
func IsNotFound(err error) bool {
return Code(err) == codes.NotFound
return Code(err) == 404
}
// Conflict new Conflict error that is mapped to a 409 response.
func Conflict(domain, reason, message string) *Error {
return Newf(codes.Aborted, domain, reason, message)
func Conflict(reason, message string) *Error {
return Newf(409, reason, message)
}
// IsConflict determines if err is an error which indicates a Conflict error.
// It supports wrapped errors.
func IsConflict(err error) bool {
return Code(err) == codes.Aborted
return Code(err) == 409
}
// InternalServer new InternalServer error that is mapped to a 500 response.
func InternalServer(domain, reason, message string) *Error {
return Newf(codes.Internal, domain, reason, message)
func InternalServer(reason, message string) *Error {
return Newf(500, reason, message)
}
// IsInternalServer determines if err is an error which indicates an Internal error.
// It supports wrapped errors.
func IsInternalServer(err error) bool {
return Code(err) == codes.Internal
return Code(err) == 500
}
// ServiceUnavailable new ServiceUnavailable error that is mapped to a HTTP 503 response.
func ServiceUnavailable(domain, reason, message string) *Error {
return Newf(codes.Unavailable, domain, reason, message)
func ServiceUnavailable(reason, message string) *Error {
return Newf(503, reason, message)
}
// IsServiceUnavailable determines if err is an error which indicates a Unavailable error.
// It supports wrapped errors.
func IsServiceUnavailable(err error) bool {
return Code(err) == codes.Unavailable
return Code(err) == 503
}

View File

@@ -5,13 +5,13 @@ import "testing"
func TestTypes(t *testing.T) {
var (
input = []error{
BadRequest("domain_400", "reason_400", "message_400"),
Unauthorized("domain_401", "reason_401", "message_401"),
Forbidden("domain_403", "reason_403", "message_403"),
NotFound("domain_404", "reason_404", "message_404"),
Conflict("domain_409", "reason_409", "message_409"),
InternalServer("domain_500", "reason_500", "message_500"),
ServiceUnavailable("domain_503", "reason_503", "message_503"),
BadRequest("reason_400", "message_400"),
Unauthorized("reason_401", "message_401"),
Forbidden("reason_403", "message_403"),
NotFound("reason_404", "message_404"),
Conflict("reason_409", "message_409"),
InternalServer("reason_500", "message_500"),
ServiceUnavailable("reason_503", "message_503"),
}
output = []func(error) bool{
IsBadRequest,