【问题标题】:Using a custom Rocket Responder for an error in a RequestGuard使用自定义 Rocket Responder 处理 RequestGuard 中的错误
【发布时间】:2019-09-20 14:31:36
【问题描述】:

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

但是,我在RequestGuards 中找不到使用这些错误响应的方法。似乎from_request 函数导致Outcome 使用完全不同的模型,错误时返回Outcome::Failure((Status, T))

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

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

【问题讨论】:

    标签: rust rust-rocket


    【解决方案1】:

    docs for FromRequest's Outcome 状态:

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

    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
            }
        }
    }
    

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

    【讨论】:

      猜你喜欢
      • 2016-12-14
      • 1970-01-01
      • 2019-10-19
      • 2018-10-12
      • 2020-10-27
      • 2013-02-12
      • 2011-01-29
      • 2010-12-07
      • 1970-01-01
      相关资源
      最近更新 更多