2 KiB
title | date | draft | toc | images | tags | ||
---|---|---|---|---|---|---|---|
actix-web Url Dispatch and Middleware | 2022-04-24T03:37:47-04:00 | false | false |
|
I've hit an issue with actix-web
recently, and ended up learning a bit about
how it does routing (or URL dispatch as they name it).
Here's a simplified version of my problem:
// service code
#[get("/{path:.*}")]
pub async fn basic_handler(params: web::Path<String>) -> HttpResponse {
// ...
}
#[post("/auth/")]
pub async fn auth_handler() -> HttpResponse {
// ...
}
// in main
let auth_service = web::scope("")
.wrap(auth_middleware)
.service(auth_handler);
App::new()
.service(authenticated_scope)
.service(basic_handler)
auth_middleware
is a custom middleware I wrote which checks for the existence
of an auth token in a header or cookie, then validates the token. The middleware
responds early with a 401 if the token is missing, which ensures that the
protected handlers can only be reached by authenticated users.
I expected Actix to realize that if a request doesn't match /auth/
, it should
go to the basic_handler
and the authentication middleware shouldn't run. But
the middleware did run even if the path had nothing to do with /auth/
! That
would cause the middleware to respond with a 401 and stops it from propagating,
so it never reached basic_handler
.
The solution, it turns out, is using the web::scope
to scope out the
authenticated routes. If the scope doesn't match, Actix then seems to skip over
that entire scope and never runs the middleware. Here's the same code, fixed:
// service code
#[get("/{path:.*}")]
pub async fn basic_handler(params: web::Path<String>) -> HttpResponse {
// ...
}
#[post("/")] // <-- change here
pub async fn auth_handler() -> HttpResponse {
// ...
}
// in main
let auth_service = web::scope("/auth") // <-- change here
.wrap(auth_middleware)
.service(auth_handler);
App::new()
.service(authenticated_scope)
.service(basic_handler)