使用自定义 Rocket Responder 来处理 RequestGuard 中的错误

Tom*_*mas 2 rust rust-rocket

Responder在使用 Rocket.rs 的 Web 服务器应用程序中,我使用在整个 API 中实现的错误类型。此错误类型可确保统一呈现所有错误(如 RFC 7807 json)。

但是,我找不到在RequestGuards. 该from_request函数似乎会导致Outcome使用完全不同的模型,并返回Outcome::Failure((Status, T))错误。

如何确保这些请求防护中的错误以相同的 JSON 格式呈现?它甚至可以定制吗?

我尝试使用捕手,但这似乎没有检索任何错误信息。

小智 6

文档FromRequestOutcome状态:

Result<S, E>请注意,用户可以请求和的类型Option<S>来捕获Failure并检索错误值。

  1. 在实施开始时FromRequest,定义type Error = JsonValue;

  2. from_request函数中,确保它返回您要实现的目标request::Outcome<S, Self::Error>S

  3. from_request函数中,当您想要返回失败时,请执行类似Outcome::Failure((Status::Unauthorized, json!({"error": "unauthorised"})))或任何您想要返回的操作。

  4. 在您的路线函数中,用作Result<S, JsonValue>请求防护的类型,S您在哪里实现。在您的路线中,使用match将其与Ok(S)Err(json_error)例如进行匹配。

可能有一种方法可以传递 的状态Outcome::Failure,但我描述的解决方案意味着如果您使用自定义响应程序,您将在响应程序中设置状态,而不是基于Outcome::Failure- 例如下面的代码。

ApiKey以下是文档中应用于请求保护示例的示例,其中调用了一个示例自定义响应程序ApiResponse来设置其自己的状态:

#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;
#[macro_use]
extern crate serde_derive;

use rocket::Outcome;
use rocket::http::{ContentType, Status};
use rocket::request::{self, Request, FromRequest};
use rocket::response::{self, Responder, Response};
use rocket_contrib::json::{Json, JsonValue};

#[derive(Debug)]
pub struct ApiResponse {
    pub json: JsonValue,
    pub status: Status,
}

impl<'r> Responder<'r> for ApiResponse {
    fn respond_to(self, req: &Request) -> response::Result<'r> {
        Response::build_from(self.json.respond_to(req).unwrap())
            .status(self.status)
            .header(ContentType::JSON)
            .ok()
    }
}

#[derive(Debug, Deserialize, Serialize)]
struct ApiKey(String);

/// Returns true if `key` is a valid API key string.
fn is_valid(key: &str) -> bool {
    key == "valid_api_key"
}

impl<'a, 'r> FromRequest<'a, 'r> for ApiKey {
    type Error = JsonValue;

    fn from_request(request: &'a Request<'r>) -> request::Outcome<Self, Self::Error> {
        let keys: Vec<_> = request.headers().get("x-api-key").collect();
        match keys.len() {
            0 => Outcome::Failure((Status::BadRequest, json!({ "error": "api key missing" }))),
            1 if is_valid(keys[0]) => Outcome::Success(ApiKey(keys[0].to_string())),
            1 => Outcome::Failure((Status::BadRequest, json!({ "error": "api key invalid" }))),
            _ => Outcome::Failure((Status::BadRequest, json!({ "error": "bad api key count" }))),
        }
    }
}

#[get("/sensitive")]
fn sensitive(key: Result<ApiKey, JsonValue>) -> ApiResponse {
    match key {
        Ok(_ApiKey) => ApiResponse {
            json: json!({ "data": "sensitive data." }),
            status: Status::Ok
        },
        Err(json_error) => ApiResponse {
            json: json_error,
            status: Status::BadRequest
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我是 Rust 和 Rocket 的新手,所以这可能不是最好的解决方案。