You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
114 lines
2.4 KiB
114 lines
2.4 KiB
4 years ago
|
package status
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
|
||
|
"github.com/go-kratos/kratos/v2/errors"
|
||
|
"github.com/go-kratos/kratos/v2/middleware"
|
||
|
"github.com/golang/protobuf/proto"
|
||
|
"github.com/golang/protobuf/ptypes"
|
||
|
"google.golang.org/genproto/googleapis/rpc/errdetails"
|
||
|
"google.golang.org/grpc/codes"
|
||
|
"google.golang.org/grpc/status"
|
||
|
)
|
||
|
|
||
|
// HandlerFunc is middleware error handler.
|
||
|
type HandlerFunc func(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: errorEncode,
|
||
|
}
|
||
|
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(err)
|
||
|
}
|
||
|
return reply, nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Client is an error middleware.
|
||
|
func Client(opts ...Option) middleware.Middleware {
|
||
|
options := options{
|
||
|
handler: errorDecode,
|
||
|
}
|
||
|
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(err)
|
||
|
}
|
||
|
return reply, nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func errorEncode(err error) error {
|
||
|
se, ok := errors.FromError(err)
|
||
|
if !ok {
|
||
|
se = &errors.StatusError{
|
||
|
Code: 2,
|
||
|
}
|
||
|
}
|
||
|
gs := status.Newf(codes.Code(se.Code), "%s: %s", se.Reason, se.Message)
|
||
|
details := []proto.Message{
|
||
|
&errdetails.ErrorInfo{
|
||
|
Reason: se.Reason,
|
||
|
Metadata: map[string]string{"message": se.Message},
|
||
|
},
|
||
|
}
|
||
|
for _, any := range se.Details {
|
||
|
detail := &ptypes.DynamicAny{}
|
||
|
if err := ptypes.UnmarshalAny(any, detail); err != nil {
|
||
|
continue
|
||
|
}
|
||
|
details = append(details, detail.Message)
|
||
|
}
|
||
|
gs, err = gs.WithDetails(details...)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
return gs.Err()
|
||
|
}
|
||
|
|
||
|
func errorDecode(err error) error {
|
||
|
gs := status.Convert(err)
|
||
|
se := &errors.StatusError{
|
||
|
Code: int32(gs.Code()),
|
||
|
Details: gs.Proto().Details,
|
||
|
}
|
||
|
for _, detail := range gs.Details() {
|
||
|
switch d := detail.(type) {
|
||
|
case *errdetails.ErrorInfo:
|
||
|
se.Reason = d.Reason
|
||
|
se.Message = d.Metadata["message"]
|
||
|
return se
|
||
|
}
|
||
|
}
|
||
|
return se
|
||
|
}
|