首页 > 解决方案 > 将域结果映射到 http 结果

问题描述

我正在尝试在 Rust 中使用 Actix Web 框架构建 HTTP 服务器。我习惯于将业务模型和业务错误与 HttpResponse 分开。为此,我有我的服务 CredentialService,它公开了一个返回结果的方法Result<String, CredentialServiceError>

我的 WebServer 公开了一个POST /login接受usernamepassword返回 JWT 的 API。

枚举“CredentialServiceError”如下

use derive_more::{Display, Error};

#[derive(Debug, Display, Error)]
pub enum CredentialServiceError {
    NoCredentialFound,
    ErrorOnGeneratingJWT,
}

我的处理程序是这样的:

async fn login(request_body: web::Json<LoginRequest>, credential_service: web::Data<CredentialService>) -> Result<HttpResponse> {
    let request_body: LoginRequest = request_body.0;

    let jwt = credential_service.login(request_body.username, request_body.password);

    let response: Result<LoginResponse, CredentialServiceError> = jwt.map(|jwt| {
        LoginResponse { jwt }
    });
    response.into()
}

我收到此错误:

the trait bound `std::result::Result<actix_web::HttpResponse, actix_web::Error>: std::convert::From<std::result::Result<model::LoginResponse, credential_service::CredentialServiceError>>` is not satisfied

the trait `std::convert::From<std::result::Result<model::LoginResponse, credential_service::CredentialServiceError>>` is not implemented for `std::result::Result<actix_web::HttpResponse, actix_web::Error>`

help: the following implementations were found:
        <std::result::Result<(), idna::uts46::Errors> as std::convert::From<idna::uts46::Errors>>
        <std::result::Result<(), ring::error::Unspecified> as std::convert::From<ring::bssl::Result>>
        <std::result::Result<miniz_oxide::MZStatus, miniz_oxide::MZError> as std::convert::From<&miniz_oxide::StreamResult>>
        <std::result::Result<miniz_oxide::MZStatus, miniz_oxide::MZError> as std::convert::From<&miniz_oxide::StreamResult>>
      and 2 others
note: required because of the requirements on the impl of `std::convert::Into<std::result::Result<actix_web::HttpResponse, actix_web::Error>>` for `std::result::Result<model::LoginResponse, credential_service::CredentialServiceError>`rustc(E0277)

我也尝试过实施

impl error::ResponseError for CredentialServiceError { ... }
impl Into<HttpResponse> for LoginResponse { ... }

错误没有改变。

那么,我怎样才能转换Result<String, CredentialServiceError>Result<HttpResponse>?

标签: rustactix-webrust-actix

解决方案


因此,您面临的问题是,您希望将结果转换为 actix_web 函数期望的结果。我希望我理解正确。

除了将您的结果映射到预期的结果之外,我没有看到其他方法。有不同的方法可以实现这一点。如果你想在你的LoginResponse结构中保留实现细节,你可以使用Into特征。

impl Into<HttpResponse> for LoginResponse {
    fn into(self) -> HttpResponse {
        HttpResponse::Ok().body(self.jwt)
    }
}

如果您不在乎,可以使用提供的地图功能Result

fn map_credential_error(error: CredentialServiceError) -> actix_web::error::Error {
    match error {
        CredentialServiceError::NoCredentialFound => {
            actix_web::error::ErrorUnauthorized("Not authorized")
        }
        CredentialServiceError::ErrorOnGeneratingJWT => {
            actix_web::error::ErrorInternalServerError("Something went wrong generating your jwt")
        }
    }
}

所以最后你的登录功能看起来像这样。

async fn login(
    request_body: web::Json<LoginRequest>,
    credential_service: web::Data<CredentialService>,
) -> Result<HttpResponse> {
    let request_body: LoginRequest = request_body.0;

    let jwt = credential_service.login(request_body.username, request_body.password);

    let response: Result<HttpResponse, CredentialServiceError> =
        jwt.map(|jwt| LoginResponse { jwt }.into());

    response.map_err(map_credential_error)
}

希望我能提供帮助,并且我理解你的问题。


推荐阅读