2

我用 actix 制作了一个 web 服务,我正在尝试使用 Actix-Web 1.0 - Complete Tutorial 来实现 Auth Web Microservice with Rust中的身份验证流程:

use std::sync::Arc;
use std::sync::Mutex;

use actix_cors::Cors;
use actix_identity::{CookieIdentityPolicy, Identity, IdentityService};

use actix::prelude::*;
use actix::{Actor, SyncContext};
use actix_web::{
    dev::Payload, error, guard, http::header, middleware, web, App, Error as AWError, FromRequest,
    HttpRequest, HttpResponse, HttpServer, Responder, ResponseError,
};
use derive_more::Display;

use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TokenResult {
    pub company: String,
    pub config: String,
}

#[derive(Debug, Display)]
pub enum ServiceError {
    #[display(fmt = "Internal Server Error")]
    InternalServerError(String),

    #[display(fmt = "BadRequest: {}", _0)]
    BadRequest(String),

    #[display(fmt = "Unauthorized")]
    Unauthorized,
}

impl ResponseError for ServiceError {
    fn error_response(&self) -> HttpResponse {
        match *self {
            ServiceError::InternalServerError(ref message) => {
                HttpResponse::InternalServerError().json(message)
            }
            ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message),
            ServiceError::Unauthorized => HttpResponse::Unauthorized().json("Unauthorized"), //This is executed
        }
    }
}

impl FromRequest for TokenResult {
    type Error = AWError;
    type Future = Result<TokenResult, Self::Error>;
    type Config = ();

    fn from_request(req: &HttpRequest, pl: &mut Payload) -> Self::Future {
        if let Some(identity) = Identity::from_request(req, pl)?.identity() {
            let user: TokenResult = serde_json::from_str(&identity)?;
            return Ok(user);
        }
        //This is executed
        Err(ServiceError::Unauthorized.into())
    }
}

fn client_config(token: TokenResult) -> HttpResponse {
    HttpResponse::Ok().json(token.config)
}

fn main() -> Result<(), AWError> {
    let sys = actix_rt::System::new("Api");

    let mut server = HttpServer::new(move || {
        App::new()
            .wrap(IdentityService::new(
                CookieIdentityPolicy::new(&[0; 32])
                    .name("auth-cookie")
                    .secure(false),
            ))
            .service(web::resource("/config").to(client_config))
    });

    server = server.bind("127.0.0.1:8080").unwrap();

    server.start();
    println!("Started http client: 127.0.0.1:8080");
    sys.run()?;
    Ok(())
}

当我使用 httpie 和其他人进行测试时,我得到的是文本而不是 JSON:

http -v  POST http://localhost:8080/config
POST /config HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 0
Host: localhost:8080
User-Agent: HTTPie/1.0.2


HTTP/1.1 401 Unauthorized
content-length: 12
content-type: text/plain
date: Tue, 10 Sep 2019 20:41:46 GMT

Unauthorized

TokenResult除了返回 JSON之外的所有其他方法和错误。

我的依赖:

[dependencies]
actix = "0.8.3"
actix-files = "0.1.4"
actix-rt = "0.2.5"
actix-identity = "0.1.0"
actix-cors = "0.1.0"
actix-web = {version = "1.0.7", features=["flate2-rust"], default-features = false}
derive_more = "0.15.0"
futures = "0.1.29"
json = "0.12.0"
serde = { version = "1.0.100", features = ["derive"] }
serde_json = "1.0.40"
snafu = "0.5.0"
4

1 回答 1

1

有必要覆盖render_response

impl ResponseError for ServiceError {
    fn error_response(&self) -> HttpResponse {
        match *self {
            ServiceError::InternalServerError(ref message) => {
                HttpResponse::InternalServerError().json(message)
            }
            ServiceError::BadRequest(ref message) => HttpResponse::BadRequest().json(message),
            ServiceError::Unauthorized => HttpResponse::Unauthorized().json("Unauthorized"),
        }
    }

    fn render_response(&self) -> HttpResponse {
        self.error_response()
    }
}

感谢Reddit 上的 ddboline指出解决方案。

于 2019-09-11T16:08:31.750 回答