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 |
package errors |
||||||
|
|
||||||
import ( |
import ( |
||||||
"errors" |
"google.golang.org/grpc/codes" |
||||||
"fmt" |
|
||||||
) |
) |
||||||
|
|
||||||
// ErrorInfo is describes the cause of the error with structured details.
|
// BadRequest new BadRequest error that is mapped to a 400 response.
|
||||||
// For more details see https://github.com/googleapis/googleapis/blob/master/google/rpc/error_details.proto.
|
func BadRequest(domain, reason, message string) *Error { |
||||||
type ErrorInfo struct { |
return Newf(codes.InvalidArgument, domain, reason, message) |
||||||
err *Error |
|
||||||
Domain string `json:"domain"` |
|
||||||
Reason string `json:"reason"` |
|
||||||
Metadata map[string]string `json:"metadata"` |
|
||||||
} |
} |
||||||
|
|
||||||
func (e *ErrorInfo) Error() string { |
// IsBadRequest determines if err is an error which indicates a BadRequest error.
|
||||||
return fmt.Sprintf("error: domain = %s reason = %s", e.Domain, e.Reason) |
// 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) |
||||||
|
} |
||||||
|
|
||||||
|
// 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 |
||||||
|
} |
||||||
|
|
||||||
|
// 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 |
||||||
} |
} |
||||||
|
|
||||||
// Is matches each error in the chain with the target value.
|
// ServiceUnavailable new ServiceUnavailable error that is mapped to a HTTP 503 response.
|
||||||
func (e *ErrorInfo) Is(err error) bool { |
func ServiceUnavailable(domain, reason, message string) *Error { |
||||||
if target := new(ErrorInfo); errors.As(err, &target) { |
return Newf(codes.Unavailable, domain, reason, message) |
||||||
return target.Domain == e.Domain && target.Reason == e.Reason |
|
||||||
} |
|
||||||
return false |
|
||||||
} |
} |
||||||
|
|
||||||
// WithMetadata with an MD formed by the mapping of key, value.
|
// IsServiceUnavailable determines if err is an error which indicates a Unavailable error.
|
||||||
func (e *ErrorInfo) WithMetadata(md map[string]string) *ErrorInfo { |
// It supports wrapped errors.
|
||||||
err := *e |
func IsServiceUnavailable(err error) bool { |
||||||
err.Metadata = md |
return Code(err) == codes.Unavailable |
||||||
return &err |
|
||||||
} |
} |
||||||
|
@ -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