如何在actix_web中间件中正确返回错误响应?
Actix-web认证中间件无法返回错误响应:BoxBody转换问题
问题背景
开发基于actix-web的认证中间件时,认证通过的情况下能正常返回OK响应,但认证失败时尝试返回401错误响应却编译报错。以下是实现代码:
/// ResponseBody是自定义的错误/内容结构体 #[derive(Debug, Serialize, Deserialize)] pub struct ResponseBody<T> { pub message: String, pub data: T, } impl<T> ResponseBody<T> { pub fn new(message: &str, data: T) -> ResponseBody<T> { ResponseBody { message: message.to_string(), data, } } } /// 中间件中的逻辑 if authenticate_pass { let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; Ok(res) }) } else { Box::pin(async move { Ok(req.into_response( HttpResponse::Unauthorized() .json(ResponseBody::new("Session ends. Please login again", "")) .into_body(), )) }) }
编译时出现如下错误:
error[E0277]: the trait bound `actix_web::dev::Response<_>: std::convert::From<BoxBody>` is not satisfied --> src\auth.rs:103:21 | 102 | Ok(req.into_response( | ------------- required by a bound introduced by this call 103 | / HttpResponse::Unauthorized() 104 | | .json(ResponseBody::new("Session ends. Please login again", "")) 105 | | .into_body(), | |____________________________________^ the trait `std::convert::From<BoxBody>` is not implemented for `actix_web::dev::Response<_>` | = help: the following other types implement trait `std::convert::From<T>`: <actix_web::dev::Response<&'static [u8]> as std::convert::From<&'static [u8]> > <actix_web::dev::Response<&'static str> as std::convert::From<&'static str> > <actix_web::dev::Response<B> as std::convert::From<HttpResponse<B>> > <actix_web::dev::Response<B> as std::convert::From<ServiceResponse<B>> > <actix_web::dev::Response<BoxBody> as std::convert::From<&actix_http::ws::HandshakeError>> > <actix_web::dev::Response<BoxBody> as std::convert::From<HttpResponseBuilder>> > <actix_web::dev::Response<BoxBody> as std::convert::From<Infallible>> > <actix_web::dev::Response<BoxBody> as std::convert::From<Result<I, E>> > and 13 others = note: required because of the requirements on the impl of `Into<actix_web::dev::Response<_>>` for `BoxBody` note: required by a bound in `ServiceRequest::into_response` --> C:\Users\mdenn\.cargo\registry\src\github.com-1ecc6299db9ec823\actix-web-4.1.0\src\service.rs:144:32 | 144 | pub fn into_response<B, R: Into<Response<B>>>(self, res: R) -> ServiceResponse<B> { | ^^^^^^^^^^^^^^^^^ required by this bound in `ServiceRequest::into_response`
使用的依赖版本:
actix-web = "4.1.0" futures = "0.3.24" tokio = { version = "1.18.2", features = ["full"] } serde = { version = "1.0.137", features = ["derive"] }
错误原因分析
错误的核心是错误地调用了.into_body()方法:
HttpResponse::Unauthorized().json(...)返回的是一个完整的HttpResponse<BoxBody>实例,它本身已经实现了Into<Response<BoxBody>>trait,符合into_response方法的参数要求。- 调用
.into_body()后,把完整的HttpResponse转换成了BoxBody类型(仅响应体部分),而Response<_>并没有实现从BoxBody直接转换的trait,导致类型不匹配。
解决方案
直接去掉.into_body()调用,将完整的HttpResponse实例传给req.into_response()即可。
修正后的代码
/// ResponseBody是自定义的错误/内容结构体 #[derive(Debug, Serialize, Deserialize)] pub struct ResponseBody<T> { pub message: String, pub data: T, } impl<T> ResponseBody<T> { pub fn new(message: &str, data: T) -> ResponseBody<T> { ResponseBody { message: message.to_string(), data, } } } /// 中间件中的逻辑 if authenticate_pass { let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; Ok(res) }) } else { Box::pin(async move { Ok(req.into_response( HttpResponse::Unauthorized() .json(ResponseBody::new("Session ends. Please login again", "")) // 移除.into_body()调用 )) }) }
关于BoxBody的补充说明
BoxBody是actix-web中用于封装响应体的动态类型,它是Body trait的装箱实现,允许在运行时使用不同的响应体类型。在大多数场景下,你不需要直接操作BoxBody,HttpResponse构建器会自动处理响应体的封装,直接传递HttpResponse实例给相关方法即可满足类型要求。
内容的提问来源于stack exchange,提问作者DennyHiu
相关产品推荐
相关产品推荐

