Overlapping scopes work differently than flat overlapping routes
- Lenguaje dominante
- Rust
- Estrellas
- 24.8k
- Forks
- 1.9k
- Merge medio
- 23 h 10 min
- PR fusionados (30 d)
- 26
Descripción
## Expected Behavior
The docs seem to suggest that the `web::scope(...)` is a helper that groups different routes under the same prefix, but that otherwise the behaviour would be the same as with flattened routes. At least, I didn't see any mention of differences.
So, when I refactored my code from flat routes like in this minimal example:
```rust
use actix_web::{get, put, web, App, HttpServer, Responder};
#[get("/camera/dimensions")]
async fn camera_dimensions() -> impl Responder {
"800x600"
}
#[get("/camera/owner")]
async fn camera_owner() -> impl Responder {
"John Doe"
}
#[get("/focuser/distance")]
async fn focuser_focus() -> impl Responder {
"42"
}
#[put("/focuser/distance")]
async fn focuser_focus_set(value: web::Path) -> impl Responder {
format!("Set focus to {}", value)
}
#[get("/{device_type}/connected")]
async fn device_connected(device_type: web::Path) -> impl Responder {
format!("{} is connected", device_type)
}
#[put("/{device_type}/connect")]
async fn device_connect(device_type: web::Path) -> impl Responder {
format!("Connecting {}", device_type)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(camera_dimensions)
.service(camera_owner)
.service(focuser_focus)
.service(focuser_focus_set)
.service(device_connected)
.service(device_connect)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
```
to scoped groups like in this one:
```rust
use actix_web::{get, put, web, App, HttpServer, Responder};
#[get("/dimensions")]
async fn camera_dimensions() -> impl Responder {
"800x600"
}
#[get("/owner")]
async fn camera_owner() -> impl Responder {
"John Doe"
}
#[get("/distance")]
async fn focuser_focus() -> impl Responder {
"42"
}
#[put("/distance")]
async fn focuser_focus_set(value: web::Path) -> impl Responder {
format!("Set focus to {}", value)
}
#[get("/connected")]
async fn device_connected(device_type: web::Path) -> impl Responder {
format!("{} is connected", device_type)
}
#[put("/connect")]
async fn device_connect(device_type: web::Path) -> impl Responder {
format!("Connecting {}", device_type)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.service(
web::scope("/camera")
.service(camera_dimensions)
.service(camera_owner),
)
.service(
web::scope("/focuser")
.service(focuser_focus)
.service(focuser_focus_set),
)
.service(
web::scope("/{device_type}")
.service(device_connected)
.service(device_connect),
)
})
.bind(("127.0.0.1", 8080))?
.run()
.await
}
```
I expected all routes to continue working like before.
## Current Behavior
Unfortunately, it seems that scopes are only matched against each other, and, once a scope is matched, no other scopes are tried even if it doesn't have the required method.
So, routes like `/camera/connected` and `/focuser/connected` worked before, but stop working after refactoring.
I thought reordering might help, putting the generic `/{device_type}` scope first, but that only seems to change which scope catches everything - now, routes like `/*/connected` work but specific device routes like `/camera/dimensions` don't.
Finally, I thought maybe I should use `/{device_type}` as `app.default_service()`, but that doesn't work either as `default_service()` seems to only accept a single handler and not a scope as its argument.
## Possible Solution
Flatten all routes at the app level when scopes are registered, so that there is no observable change in behaviour after grouping.
## Steps to Reproduce (for bugs)
Run the minimal examples provided above with suggested requests like `http://localhost:8080/camera/connected` and observe the working response "camera is connected" in the first implementation and 404 error in the 2nd one.
## Context
I'm trying to refactor my complex list of routes into grouped scopes in separate functions to make code a bit more maintainable.
## Your Environment
- Rust Version (I.e, output of `rustc -V`): rustc 1.63.0 (4b91a6ea7 2022-08-08)
- Actix Web Version: 4.2.1
Guía de contribución
Evaluación
Este issue todavía no se ha evaluado.