73 lines
2 KiB
Markdown
73 lines
2 KiB
Markdown
---
|
|
title: "actix-web Url Dispatch and Middleware"
|
|
date: 2022-04-24T03:37:47-04:00
|
|
draft: false
|
|
toc: false
|
|
images:
|
|
tags:
|
|
- dev
|
|
- rust
|
|
---
|
|
|
|
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:
|
|
|
|
```rust
|
|
// 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:
|
|
|
|
```rust
|
|
// 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)
|
|
```
|