You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 03:37:05