如何从 actix-web 中间件返回早期响应? [英] How to return an early response from an actix-web middleware?

查看:158
本文介绍了如何从 actix-web 中间件返回早期响应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的客户通过 Authorization 标头中的令牌进行授权,每个请求都需要检查该令牌.如果没有这个header或者我找不到对应的用户,我想返回HTTP代码Unauthorized,否则我想正常处理请求.

My clients authorize through a token in the Authorization header which needs to be checked for each request. If this header is missing or I cannot find a corresponding user, I want to return the HTTP code Unauthorized, else I want to handle the request normally.

目前我有很多重复的代码,因为我正在每个请求处理程序中检查这个标头.actix docs 在第一段中建议可以 停止请求处理以返回尽早响应.如何实现?

Currently I have a lot of duplicate code because I am checking for this header in every request handler. The actix docs suggest in the very first paragraph that it is possible to halt request processing to return a response early. How can this be achieved?

由于我还没有找到实现此行为的示例,因此我尝试提出自己的中间件函数,但无法编译.

Since I have not found an example that implements this behavior I tried to come up with my own middleware function, but it won't compile.

为了克服返回两种不同类型(ServiceResponseMap)的问题,我已经将返回值装箱了,所以问题在 如何有条件地返回不同类型的期货? 不是问题.更重要的是,我不知道哪些类型具有哪些特征实现作为此 wrap_fn 函数的返回值.我现在拥有的那些不起作用.

I have already boxed the return values in order to overcome the problem of returning two different types (ServiceResponse and Map), so the problem asked in How do I conditionally return different types of futures? is not the issue. It is more that I do not know which types with which trait implementations are exactly required as return value for this wrap_fn function. The ones I have right now do not work.

App::new()
    .wrap(Cors::new().allowed_origin("http://localhost:8080"))
    .register_data(state.clone())
    .service(
        web::scope("/routing")
            .wrap_fn(|req, srv| {
                let unauth: Box<dyn IntoFuture<Item = ServiceResponse>> = Box::new(ServiceResponse::new(req.into_parts().0, HttpResponse::Unauthorized().finish()));
                let auth_header = req.headers().get("Authorization");
                match auth_header {
                    None => unauth,
                    Some(value) => {
                        let token = value.to_str().unwrap();
                        let mut users = state.users.lock().unwrap();
                        let user_state = users.iter_mut().find(|x| x.auth.token == token);
                        match user_state {
                            None => unauth,
                            Some(user) => {
                                Box::new(srv.call(req).map(|res| res))
                            }
                        }
                    }
                }
            })
            .route("/closest", web::get().to(routing::find_closest))
            .route("/fsp", web::post().to(routing::fsp))
            .route("/preference", web::get().to(routing::get_preference))
            .route("/preference", web::post().to(routing::set_preference))
            .route("/find_preference", web::post().to(routing::find_preference))
            .route("/reset", web::post().to(routing::reset_data)),
    )
    .bind("0.0.0.0:8000")
    .expect("Can not bind to port 8000")
    .run()
    .expect("Could not start sever");

我在编译时遇到了两个错误.

There are two errors that I am getting upon compiling.

1.

error[E0191]: the value of the associated types `Future` (from the trait `futures::future::IntoFuture`), `Error` (from the trait `futures::future::IntoFuture`) must be specified
  --> src/server/mod.rs:36:41
   |
36 |                         let unauth: Box<dyn IntoFuture<Item = ServiceResponse>> =
   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |                                         |
   |                                         associated type `Future` must be specified
   |                                         associated type `Error` must be specified

2.

error[E0277]: the trait bound `dyn futures::future::IntoFuture<Item = actix_web::service::ServiceResponse>: futures::future::Future` is not satisfied
  --> src/server/mod.rs:35:22
   |
35 |                     .wrap_fn(|req, srv| {
   |                      ^^^^^^^ the trait `futures::future::Future` is not implemented for `dyn futures::future::IntoFuture<Item = actix_web::service::ServiceResponse>`
   |
   = note: required because of the requirements on the impl of `futures::future::Future` for `std::boxed::Box<dyn futures::future::IntoFuture<Item = actix_web::service::ServiceResponse>>`

推荐答案

可以创建自己的类型,Authorized,实现FromRequest 并将 Authorized 定义为处理程序中的参数应该检查授权.

You can create your own type, Authorized, implement FromRequest for it and define Authorized as an argument in the handlers that should be checked for authorization.

简化示例:

use actix_web::dev::Payload;
use actix_web::error::ErrorUnauthorized;
use actix_web::{web, App, Error, FromRequest, HttpRequest, HttpResponse, HttpServer};

fn main() {
    HttpServer::new(move || App::new().route("/", web::to(index)))
        .bind("127.0.0.1:3000")
        .expect("Can not bind to '127.0.0.1:3000'")
        .run()
        .unwrap();
}

fn index(_: Authorized) -> HttpResponse {
    HttpResponse::Ok().body("authorized")
}

struct Authorized;

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

    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
        if is_authorized(req) {
            Ok(Authorized)
        } else {
            Err(ErrorUnauthorized("not authorized"))?
        }
    }
}

fn is_authorized(req: &HttpRequest) -> bool {
    if let Some(value) = req.headers().get("authorized") {
        // actual implementation that checks header here
        dbg!(value);
        true
    } else {
        false
    }
}

此代码产生:

$ curl localhost:3000
not authorized⏎
$ curl localhost:3000 -H 'Authorized: i am root'
authorized⏎

你可能可以用中间件做同样的事情,但我对中间件抽象一无所知.此外,您可能希望向处理程序提供有用的信息,例如用户名:

You could probably do something in the same lines with middlewares, but I have not got my head around the middleware abstraction. Also, you might want to provide useful information to the handlers, like username:

struct Authorized {
    username: String
}

这篇关于如何从 actix-web 中间件返回早期响应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆