Actix Web中间件类型不匹配:期望HttpResponse<B>,实际得到HttpResponse
解决Actix Web JWT中间件的HttpResponse类型不匹配问题
问题描述
我尝试用Actix Web实现JWT验证中间件,逻辑是从请求头提取Bearer Token验证,通过后将用户结构体注入请求,失败则返回错误。但运行时出现类型不匹配错误:
impl<S, B> Service<ServiceRequest> for JwtMiddlewareMiddleware<S> | - this type parameter ... 125 | Ok::<ServiceResponse<B>, Error>(ServiceResponse::new( | -------------------- arguments to this function are incorrect 126 | req.request().clone(), 127 | new_response, | ^^^^^^^^^^^^ expected `HttpResponse<B>`, found `HttpResponse` | = note: expected struct `HttpResponse<B>` found struct `HttpResponse<BoxBody>` note: associated function defined here
原中间件代码:
use actix_service::Service; use actix_web::error::ResponseError; use actix_web::{ dev::{ServiceRequest, ServiceResponse}, Error, HttpMessage, Result, }; use actix_web::{http, HttpResponse}; use futures::future::{ok, Ready}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation}; use serde::Deserialize; use std::pin::Pin; use std::{env, fmt}; // JWT Claims structure #[derive(Debug, Deserialize)] struct Claims { user_id: String, user_name: String, exp: usize, iat: usize, iss: String, aud: String, sub: String, typ: String, jti: String, role: String, permission: String, } #[derive(Debug)] struct UnauthorizedError { message: String, } impl UnauthorizedError { fn new(message: &str) -> Self { UnauthorizedError { message: message.to_string(), } } } impl ResponseError for UnauthorizedError { fn status_code(&self) -> http::StatusCode { http::StatusCode::UNAUTHORIZED } } impl fmt::Display for UnauthorizedError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Unauthorized: {}", self.message) } } pub struct JwtMiddleware; impl<S, B> actix_service::Transform<S, ServiceRequest> for JwtMiddleware where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone, { type Response = ServiceResponse<B>; type Error = Error; type Transform = JwtMiddlewareMiddleware<S>; type InitError = (); type Future = Ready<Result<Self::Transform, Self::InitError>>; fn new_transform(&self, service: S) -> Self::Future { ok(JwtMiddlewareMiddleware { service }) } } pub struct JwtMiddlewareMiddleware<S> { service: S, } impl<S, B> Service<ServiceRequest> for JwtMiddlewareMiddleware<S> where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone, { type Response = ServiceResponse<B>; type Error = Error; type Future = Pin<Box<dyn futures::Future<Output = Result<Self::Response, Self::Error>>>>; fn poll_ready( &self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll<Result<(), Self::Error>> { self.service.poll_ready(cx) } fn call(&self, req: ServiceRequest) -> Self::Future { // Extract the Authorization header let auth_header = req.headers().get("Authorization"); if let Some(token) = auth_header { if let Ok(token_str) = token.to_str() { if token_str.starts_with("Bearer ") { // Extract the JWT from the header let jwt = token_str[7..].to_string(); let secret_key = env::var("SECRET_KEY").expect("SECRET_KEY not set"); let secret = secret_key.as_bytes(); // Replace with your secret key // Decoding key and validation settings let decoding_key = DecodingKey::from_secret(secret); let validation = Validation { algorithms: vec![Algorithm::HS256], // Adjust the algorithm accordingly ..Default::default() }; match decode::<Claims>(&jwt, &decoding_key, &validation) { Ok(_) => { // JWT is valid, continue to the inner service let fut = self.service.call(req); Box::pin(async move { let res = fut.await?; Ok::<ServiceResponse<B>, Error>(res) }) as Self::Future } Err(_) => { // JWT is invalid, return unauthorized response let err = UnauthorizedError::new("Access denied"); let res = req.error_response(err); Box::pin(async move { let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse Ok::<ServiceResponse<B>, Error>(ServiceResponse::new( req.request().clone(), new_response, )) }) as Self::Future } } } else { // Token doesn't start with "Bearer " let err = UnauthorizedError::new("Bad Request"); let res = req.error_response(err); Box::pin(async { let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse Ok::<ServiceResponse<B>, Error>(ServiceResponse::new( req.request().clone(), new_response, )) }) as Self::Future } } else { // Token is not a valid UTF-8 string let err = UnauthorizedError::new("Bad Request"); let res = req.error_response(err); Box::pin(async { let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse Ok::<ServiceResponse<B>, Error>(ServiceResponse::new( req.request().clone(), new_response, )) }) as Self::Future } } else { // Token not found in the header let err = UnauthorizedError::new("Unauthorized"); let res = req.error_response(err); Box::pin(async { let new_response = HttpResponse::Ok().finish(); // Create a new HttpResponse Ok::<ServiceResponse<B>, Error>(ServiceResponse::new( req.request().clone(), new_response, )) }) as Self::Future } } }
解决方案
错误根源是手动创建的HttpResponse默认使用BoxBody类型,但中间件要求返回HttpResponse<B>。实际上ServiceRequest::error_response已经能直接生成符合类型要求的响应,无需手动构建ServiceResponse。同时补充验证通过后注入用户结构体的逻辑:
修改后的完整代码
use actix_service::Service; use actix_web::error::ResponseError; use actix_web::{ dev::{ServiceRequest, ServiceResponse}, Error, HttpMessage, Result, }; use actix_web::{http, HttpResponse}; use futures::future::{ok, Ready}; use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation, TokenData}; use serde::Deserialize; use std::pin::Pin; use std::{env, fmt}; // JWT Claims structure #[derive(Debug, Deserialize, Clone)] struct Claims { user_id: String, user_name: String, exp: usize, iat: usize, iss: String, aud: String, sub: String, typ: String, jti: String, role: String, permission: String, } #[derive(Debug)] struct UnauthorizedError { message: String, } impl UnauthorizedError { fn new(message: &str) -> Self { UnauthorizedError { message: message.to_string(), } } } impl ResponseError for UnauthorizedError { fn status_code(&self) -> http::StatusCode { http::StatusCode::UNAUTHORIZED } fn error_response(&self) -> HttpResponse { HttpResponse::Unauthorized().body(self.message.clone()) } } impl fmt::Display for UnauthorizedError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Unauthorized: {}", self.message) } } pub struct JwtMiddleware; impl<S, B> actix_service::Transform<S, ServiceRequest> for JwtMiddleware where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone, { type Response = ServiceResponse<B>; type Error = Error; type Transform = JwtMiddlewareMiddleware<S>; type InitError = (); type Future = Ready<Result<Self::Transform, Self::InitError>>; fn new_transform(&self, service: S) -> Self::Future { ok(JwtMiddlewareMiddleware { service }) } } pub struct JwtMiddlewareMiddleware<S> { service: S, } impl<S, B> Service<ServiceRequest> for JwtMiddlewareMiddleware<S> where S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + Clone, { type Response = ServiceResponse<B>; type Error = Error; type Future = Pin<Box<dyn futures::Future<Output = Result<Self::Response, Self::Error>>>>; fn poll_ready( &self, cx: &mut std::task::Context<'_>, ) -> std::task::Poll<Result<(), Self::Error>> { self.service.poll_ready(cx) } fn call(&self, mut req: ServiceRequest) -> Self::Future { // Extract the Authorization header let auth_header = req.headers().get("Authorization"); let result = match auth_header { Some(token) => match token.to_str() { Ok(token_str) if token_str.starts_with("Bearer ") => { let jwt = token_str[7..].to_string(); let secret_key = env::var("SECRET_KEY").expect("SECRET_KEY not set"); let secret = secret_key.as_bytes(); let decoding_key = DecodingKey::from_secret(secret); let validation = Validation { algorithms: vec![Algorithm::HS256], ..Default::default() }; match decode::<Claims>(&jwt, &decoding_key, &validation) { Ok(token_data) => { // 将用户Claims注入请求的extensions中 req.extensions_mut().insert(token_data.claims); Ok(req) } Err(_) => Err(UnauthorizedError::new("Access denied")), } } Ok(_) => Err(UnauthorizedError::new("Token must start with Bearer")), Err(_) => Err(UnauthorizedError::new("Invalid token format")), }, None => Err(UnauthorizedError::new("Authorization header missing")), }; match result { Ok(req) => { // 验证通过,继续执行后续服务 let fut = self.service.call(req); Box::pin(async move { Ok(fut.await?) }) } Err(err) => { // 验证失败,返回错误响应 Box::pin(async move { Ok(req.error_response(err)) }) } } as Self::Future } }
关键修改说明
- 修复类型不匹配:移除所有手动创建
HttpResponse和ServiceResponse::new的代码,直接返回req.error_response(err),该方法会自动生成符合ServiceResponse<B>类型的响应 - 注入用户结构体:验证成功后,将解析得到的
Claims通过req.extensions_mut().insert()注入请求,后续handler可以通过req.extensions().get::<Claims>()获取用户信息 - 优化错误处理:简化分支逻辑,统一错误返回路径,同时为
UnauthorizedError实现error_response方法,自定义错误响应内容 - 完善错误信息:细化不同错误场景的提示信息,便于调试
内容的提问来源于stack exchange,提问作者Biswas Sampad
相关产品推荐
相关产品推荐

