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

如何在Tower中间件与Tonic中正确返回错误?

解决Tower中间件+Tonic返回错误的问题

你的问题核心是错误类型不匹配:你的中间件MyMiddleware的Error类型是S::Error(底层服务的错误类型),但你直接返回的Status::internal("foo")是tonic::Status类型,两者无法直接兼容,导致编译失败。

方案一:明确底层服务的错误类型为tonic::Status

如果你的底层Tonic服务的Service实现中,Error类型就是tonic::Status,可以直接修改中间件的泛型约束,将S::Error固定为tonic::Status:

#[derive(Debug, Clone)]
struct MyMiddleware<S> {
    inner: S,
}

type BoxFuture<'a, T> = Pin<Box<dyn std::future::Future<Output = T> + Send + 'a>>;

impl<S> Service<hyper::Request<BoxBody>> for MyMiddleware<S>
where
    // 明确指定S的Error类型为tonic::Status
    S: Service<hyper::Request<BoxBody>, Response = hyper::Response<BoxBody>, Error = tonic::Status>
        + Clone
        + Send
        + 'static,
    S::Future: Send + 'static,
{
    type Response = S::Response;
    type Error = tonic::Status; // 直接指定Error类型为tonic::Status
    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, req: hyper::Request<BoxBody>) -> Self::Future {
        let clone = self.inner.clone();
        let mut inner = std::mem::replace(&mut self.inner, clone);

        Box::pin(async move {
            if condition {
                let response = inner.call(req).await?;
                Ok(response)
            } else {
                // 现在类型匹配,可以直接返回Status错误
                Err(Status::internal("foo"))
            }
        })
    }
}

方案二:实现错误类型转换

如果不想固定S::Error的类型,可以让S::Error实现From<tonic::Status> trait(或者手动转换),这样就能将Status转换成S::Error:

// 在call方法的错误分支中使用into()转换
Err(Status::internal("foo").into())

需要确保你的底层服务的错误类型支持从tonic::Status转换,比如如果S::Error是自定义错误,需要实现:

impl From<tonic::Status> for YourCustomError {
    fn from(status: tonic::Status) -> Self {
        // 自定义转换逻辑
        YourCustomError::Grpc(status)
    }
}

补充说明

Tonic依赖Tower处理请求,返回的tonic::Status会被自动转换成符合gRPC规范的Hyper响应。如果直接返回Hyper响应(比如Ok(hyper::Response::builder().status(500).body(...))),虽然能运行,但不符合gRPC的错误格式规范,不推荐使用。

内容的提问来源于stack exchange,提问作者Vana

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 19:55:11