How to return an early response from an actix-web middleware?
I'm kind of late to the party but the best way to do this from within Actix middleware is using futures::future::Either
. You can see how it's used here: https://github.com/actix/examples/blob/master/middleware/middleware/src/redirect.rs.
The left hand side of Either
will be a Future which passes the response to the next stage in the chain. The right hand side will be a response (usually HttpResponse
) if you wish to return the response early.
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.
Simplified example:
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
}
}
This code yields:
$ 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
}