grpc-gateway如何链式调用多个HTTP错误处理器?
在gRPC-Gateway中实现链式错误处理器
现有两个自定义gRPC-Gateway错误处理器
runTimeError1和runTimeError2,当前仅能将单个处理器赋值给runtime.HTTPError,希望实现类似数组形式的链式调用,让两个处理器依次执行。
原示例代码:
import "github.com/grpc-ecosystem/grpc-gateway/runtime" // first error handler var runTimeError1 = func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { stat, ok := status.FromError(err) fmt.Println("body") if !ok || len(stat.Details()) != 1 { fmt.Println("in unknown error") } statusCode := runtime.HTTPStatusFromCode(stat.Code()) fmt.Println("code", statusCode) } // another error handler var runTimeError2 = func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { stat, ok := status.FromError(err) fmt.Println("body") if !ok || len(stat.Details()) != 1 { fmt.Println("in unknown error") } statusCode := runtime.HTTPStatusFromCode(stat.Code()) fmt.Println("code", statusCode) } runtime.HTTPError = runTimeError1
实现方案
gRPC-Gateway本身没有原生支持多错误处理器的链式调用API,但可以通过自定义包装函数实现需求。核心逻辑是用一个包装函数包裹多个处理器,依次调用它们,再将包装函数赋值给runtime.HTTPError。
完整示例代码
import ( "context" "fmt" "net/http" "github.com/grpc-ecosystem/grpc-gateway/runtime" "google.golang.org/grpc/status" ) // 第一个错误处理器 var runTimeError1 = func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { stat, ok := status.FromError(err) fmt.Println("处理器1执行:body") if !ok || len(stat.Details()) != 1 { fmt.Println("处理器1:未知错误") } statusCode := runtime.HTTPStatusFromCode(stat.Code()) fmt.Println("处理器1:状态码", statusCode) } // 第二个错误处理器 var runTimeError2 = func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { stat, ok := status.FromError(err) fmt.Println("处理器2执行:body") if !ok || len(stat.Details()) != 1 { fmt.Println("处理器2:未知错误") } statusCode := runtime.HTTPStatusFromCode(stat.Code()) fmt.Println("处理器2:状态码", statusCode) } // 链式错误处理器包装函数 func chainErrorHandlers(handlers ...func(context.Context, *runtime.ServeMux, runtime.Marshaler, http.ResponseWriter, *http.Request, error)) func(context.Context, *runtime.ServeMux, runtime.Marshaler, http.ResponseWriter, *http.Request, error) { return func(ctx context.Context, mux *runtime.ServeMux, marshaler runtime.Marshaler, w http.ResponseWriter, r *http.Request, err error) { // 按顺序调用所有传入的处理器 for _, handler := range handlers { handler(ctx, mux, marshaler, w, r, err) } } } // 赋值链式处理器 func init() { runtime.HTTPError = chainErrorHandlers(runTimeError1, runTimeError2) }
注意事项
- 若需要控制链式执行的终止逻辑(比如某处理器处理后不再调用后续处理器),可给处理器添加返回值(如
bool表示是否继续),并在包装函数中判断返回值决定循环是否继续。 http.ResponseWriter的响应内容和状态码只能被写入一次,若多个处理器都尝试写入响应,会导致后续写入失败。建议仅让最后一个处理器或特定处理器负责写入响应,其他处理器仅做日志、监控等非写入操作。
内容的提问来源于stack exchange,提问作者shamon shamsudeen
相关产品推荐
相关产品推荐

