parent
8b875e43a5
commit
7c6f53132f
@ -1,80 +0,0 @@ |
||||
package errors |
||||
|
||||
import "net/http" |
||||
|
||||
// BadRequest new BadRequest error that is mapped to a 400 response.
|
||||
func BadRequest(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusBadRequest, domain, 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) == http.StatusBadRequest |
||||
} |
||||
|
||||
// Unauthorized new Unauthorized error that is mapped to a 401 response.
|
||||
func Unauthorized(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusUnauthorized, domain, 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) == http.StatusUnauthorized |
||||
} |
||||
|
||||
// Forbidden new Forbidden error that is mapped to a 403 response.
|
||||
func Forbidden(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusForbidden, domain, 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) == http.StatusForbidden |
||||
} |
||||
|
||||
// NotFound new NotFound error that is mapped to a 404 response.
|
||||
func NotFound(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusNotFound, domain, 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) == http.StatusNotFound |
||||
} |
||||
|
||||
// Conflict new Conflict error that is mapped to a 409 response.
|
||||
func Conflict(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusConflict, domain, 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) == http.StatusConflict |
||||
} |
||||
|
||||
// InternalServer new InternalServer error that is mapped to a 500 response.
|
||||
func InternalServer(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusInternalServerError, domain, reason, message) |
||||
} |
||||
|
||||
// IsInternalServer determines if err is an error which indicates an InternalServer error.
|
||||
// It supports wrapped errors.
|
||||
func IsInternalServer(err error) bool { |
||||
return Code(err) == http.StatusInternalServerError |
||||
} |
||||
|
||||
// ServiceUnavailable new ServiceUnavailable error that is mapped to a HTTP 503 response.
|
||||
func ServiceUnavailable(domain, reason, message string) *ErrorInfo { |
||||
return Errorf(http.StatusServiceUnavailable, domain, reason, message) |
||||
} |
||||
|
||||
// IsServiceUnavailable determines if err is an error which indicates a ServiceUnavailable error.
|
||||
// It supports wrapped errors.
|
||||
func IsServiceUnavailable(err error) bool { |
||||
return Code(err) == http.StatusServiceUnavailable |
||||
} |
@ -1,37 +1,82 @@ |
||||
package errors |
||||
|
||||
import ( |
||||
"errors" |
||||
"fmt" |
||||
"google.golang.org/grpc/codes" |
||||
) |
||||
|
||||
// ErrorInfo is describes the cause of the error with structured details.
|
||||
// For more details see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto.
|
||||
type ErrorInfo struct { |
||||
err *Error |
||||
Domain string `json:"domain"` |
||||
Reason string `json:"reason"` |
||||
Metadata map[string]string `json:"metadata"` |
||||
// 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 (e *ErrorInfo) Error() string { |
||||
return fmt.Sprintf("error: domain = %s reason = %s", e.Domain, e.Reason) |
||||
// 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 |
||||
} |
||||
func (e *ErrorInfo) Unwrap() error { |
||||
return e.err |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// Is matches each error in the chain with the target value.
|
||||
func (e *ErrorInfo) Is(err error) bool { |
||||
if target := new(ErrorInfo); errors.As(err, &target) { |
||||
return target.Domain == e.Domain && target.Reason == e.Reason |
||||
// 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 false |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// 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 |
||||
} |
||||
|
||||
// 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) |
||||
} |
||||
|
||||
// WithMetadata with an MD formed by the mapping of key, value.
|
||||
func (e *ErrorInfo) WithMetadata(md map[string]string) *ErrorInfo { |
||||
err := *e |
||||
err.Metadata = md |
||||
return &err |
||||
// 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 |
||||
} |
||||
|
@ -1,105 +0,0 @@ |
||||
package status |
||||
|
||||
import ( |
||||
"context" |
||||
|
||||
"github.com/go-kratos/kratos/v2/errors" |
||||
"github.com/go-kratos/kratos/v2/internal/http" |
||||
"github.com/go-kratos/kratos/v2/middleware" |
||||
|
||||
//lint:ignore SA1019 grpc
|
||||
"github.com/golang/protobuf/proto" |
||||
"google.golang.org/genproto/googleapis/rpc/errdetails" |
||||
"google.golang.org/grpc/status" |
||||
) |
||||
|
||||
// HandlerFunc is middleware error handler.
|
||||
type HandlerFunc func(context.Context, error) error |
||||
|
||||
// Option is recovery option.
|
||||
type Option func(*options) |
||||
|
||||
type options struct { |
||||
handler HandlerFunc |
||||
} |
||||
|
||||
// WithHandler with status handler.
|
||||
func WithHandler(h HandlerFunc) Option { |
||||
return func(o *options) { |
||||
o.handler = h |
||||
} |
||||
} |
||||
|
||||
// Server is an error middleware.
|
||||
func Server(opts ...Option) middleware.Middleware { |
||||
options := options{ |
||||
handler: encodeError, |
||||
} |
||||
for _, o := range opts { |
||||
o(&options) |
||||
} |
||||
return func(handler middleware.Handler) middleware.Handler { |
||||
return func(ctx context.Context, req interface{}) (interface{}, error) { |
||||
reply, err := handler(ctx, req) |
||||
if err != nil { |
||||
return nil, options.handler(ctx, err) |
||||
} |
||||
return reply, nil |
||||
} |
||||
} |
||||
} |
||||
|
||||
// Client is an error middleware.
|
||||
func Client(opts ...Option) middleware.Middleware { |
||||
options := options{ |
||||
handler: decodeError, |
||||
} |
||||
for _, o := range opts { |
||||
o(&options) |
||||
} |
||||
return func(handler middleware.Handler) middleware.Handler { |
||||
return func(ctx context.Context, req interface{}) (interface{}, error) { |
||||
reply, err := handler(ctx, req) |
||||
if err != nil { |
||||
return nil, options.handler(ctx, err) |
||||
} |
||||
return reply, nil |
||||
} |
||||
} |
||||
} |
||||
|
||||
func encodeError(ctx context.Context, err error) error { |
||||
var details []proto.Message |
||||
if target := new(errors.ErrorInfo); errors.As(err, &target) { |
||||
details = append(details, &errdetails.ErrorInfo{ |
||||
Domain: target.Domain, |
||||
Reason: target.Reason, |
||||
Metadata: target.Metadata, |
||||
}) |
||||
} |
||||
es := errors.FromError(err) |
||||
gs := status.New(http.GRPCCodeFromStatus(es.Code), es.Message) |
||||
gs, err = gs.WithDetails(details...) |
||||
if err != nil { |
||||
return err |
||||
} |
||||
return gs.Err() |
||||
} |
||||
|
||||
func decodeError(ctx context.Context, err error) error { |
||||
gs := status.Convert(err) |
||||
code := http.StatusFromGRPCCode(gs.Code()) |
||||
message := gs.Message() |
||||
for _, detail := range gs.Details() { |
||||
switch d := detail.(type) { |
||||
case *errdetails.ErrorInfo: |
||||
return errors.Errorf( |
||||
code, |
||||
d.Domain, |
||||
d.Reason, |
||||
message, |
||||
).WithMetadata(d.Metadata) |
||||
} |
||||
} |
||||
return errors.New(code, message) |
||||
} |
@ -1,17 +0,0 @@ |
||||
package status |
||||
|
||||
import ( |
||||
"context" |
||||
"testing" |
||||
|
||||
"github.com/go-kratos/kratos/v2/errors" |
||||
) |
||||
|
||||
func TestErrEncoder(t *testing.T) { |
||||
err := errors.BadRequest("test", "invalid_argument", "format") |
||||
en := encodeError(context.Background(), err) |
||||
de := decodeError(context.Background(), en) |
||||
if !errors.IsBadRequest(de) { |
||||
t.Errorf("expected %v got %v", err, de) |
||||
} |
||||
} |
Loading…
Reference in new issue