actix / actix-web

Actix Web is a powerful, pragmatic, and extremely fast web framework for Rust.
https://actix.rs
Apache License 2.0
21.59k stars 1.67k forks source link

Scope only serve the first sub-scope #2295

Open SadiinsoSnowfall opened 3 years ago

SadiinsoSnowfall commented 3 years ago

Let's consider the following setup:

// module base

#[get("/ping")]
pub async fn ping() -> impl Responder {
    HttpResponse::Ok().body("ping - ok")
}

pub fn base_router() -> Scope {
    web::scope("/")
        .service(ping)
        .service(b::router())
        .service(c::router())
}

[snip]
// create the application using
let app = App::new().service(base_router())
// module b
#[get("/testb")]
async fn test() -> HttpResponse {
    HttpResponse::Ok().body("b - test")
}

pub fn router() -> Scope {
    web::scope("/")
        .service(test)
}
// module c
#[get("/testc")]
async fn test() -> HttpResponse {
    HttpResponse::Ok().body("c - test")
}

pub fn router() -> Scope {
    web::scope("/")
        .service(test)
}
// module d
#[get("/testd")]
async fn test() -> HttpResponse {
    HttpResponse::Ok().body("d - test")
}

pub fn router() -> Scope {
    web::scope("/smth/")
        .service(test)
}

Expected Behavior

The routes /ping, /testb, and testc are all valid and served by the application.

Current Behavior

The /ping route is served (all the services defined directly in the top-level scope in general). The /testb route is served, but not the /testc one; only the first sub-scope is served: if we inverse the order of definition (see bellow), then the /testc route is served but not the /testb

This behaviour is only present if the two sub-scope have the same path or if one of the subscope is set to the "root path" (/) and is defined first :

web::scope("/")
        .service(ping) // served
        .service(b::router()) // served (root)
        .service(c::router()) // not served (root)

web::scope("/")
        .service(ping) // served
        .service(c::router()) // served (root)
        .service(b::router()) // not served (root)

web::scope("/")
        .service(ping) // served
        .service(d::router()) // served (/smth/)
        .service(c::router()) // served (root)

web::scope("/")
        .service(ping) // served
        .service(c::router()) // served
        .service(d::router()) // not served (/smth/)

Context

The only solution I found was not to use any "root" sub-scope. Though I haven't found anything in the documentation preventing me from using the root path in a sub-scope (and the application compiles & run fine).

Your Environment

Rust 1.53 rustc 1.53.0 (53cb7b09b 2021-06-17) Actix-Web 3 & 4.0.0-beta.8

robjtede commented 3 years ago

duplicate of #414, leaving open though