#rust #actix-web #rust-actix
Вопрос:
Я пытаюсь создать HTTP-сервер с использованием веб-платформы Actix в Rust. Я привык отделять бизнес-модель и бизнес-ошибку от HttpResponse. Для этого у меня есть служба CredentialService, которая предоставляет метод, возвращающий результат Result<String, CredentialServiceError>
.
Мой веб-сервер предоставляет API POST /login
, который принимает username
password
и возвращает JWT.
Перечисление `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<amp;miniz_oxide::StreamResult>>
<std::result::Result<miniz_oxide::MZStatus, miniz_oxide::MZError> as std::convert::From<amp;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>
?
Ответ №1:
Таким образом, проблема, с которой вы сталкиваетесь, заключается в том, что вы хотите преобразовать свой результат в результат, ожидаемый функциями 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)
}
Надеюсь, я мог бы помочь и что я понял ваш вопрос.
Ответ №2:
В дополнение к ответу @Julius-Kreutz вы можете рассмотреть impl ResponseError
источник признаков из Чанды, Абхишек. Например, если ваше CredentialServiceError
перечисление похоже:
#[derive(Debug, Display)]
pub enum CredentialServiceError {
#[display(fmt = "Internal Server Error")]
InternalServerError,
#[display(fmt = "BadRequest: {}", _0)]
BadRequest(String),
#[display(fmt = "JWKSFetchError")]
JWKSFetchError,
}
Тогда юо может сделать что-то вроде:
impl ResponseError for CredentialServiceError {
fn error_response(amp;self) -> HttpResponse {
match self {
CredentialServiceError::InternalServerError => {
HttpResponse::InternalServerError().json("Internal Server Error, Please try later")
}
CredentialServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message),
ServiceError::JWKSFetchError => {
HttpResponse::InternalServerError().json("Could not fetch JWKS")
}
}
}
}
Чтобы включить .into()
Комментарии:
1.
impl error::ResponseError for CredentialServiceError { ... }
разве это не одно и то же?2. @allevo Это то же самое, однако я не был уверен, как он допустил ошибку, поэтому я привел ему пример шаблона, который в сочетании с ответом Джулиуса-Крейца (выше) даст ему результат 😛