--- 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) -> 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) -> 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) ```