How to use routes attributes macros for multiple methods in Actix-Web
Asked Answered
W

4

6

In the Actix Web Framework, how does one use the route attributes macros (#[http_method("route")]) to bind multiple http methods to one function?

For example, I have this trivial endpoint:

/// Returns a UUID4.
#[get("/uuid")]
async fn uuid_v4() -> impl Responder {
    HttpResponse::Ok().json(Uuid {
        uuid: uuid::Uuid::new_v4(),
    })
}

I would like to have the same endpoint handle HEAD requests, how do I do this? My initial approach was to just stack up the macros:

/// Returns a UUID4.
#[get("/uuid")]
#[head("/uuid")]
async fn uuid_v4() -> impl Responder {
    HttpResponse::Ok().json(Uuid {
        uuid: uuid::Uuid::new_v4(),
    })
}

But I do get a compilation error:

    |
249 | async fn uuid_v4() -> impl Responder {
    |          ^^^^^^^ the trait `actix_web::handler::Factory<_, _, _>` is not implemented for `<uuid_v4 as actix_web::service::HttpServiceFactory>::register::uuid_v4`

I have gone through the actix-web and actix-web-codegen docs and didn't find anything addressing this

Warram answered 19/2, 2020 at 21:12 Comment(0)
R
7

you can do

#[route("/", method="GET", method="POST", method="PUT")]
async fn index() -> impl Responder {
  HttpResponse::Ok().body("Hello world!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
  HttpServer::new(move || {
    App::new()
        .service(index)
  })
  .bind("127.0.0.1:8080")?
  .run()
  .await
}
Reinstate answered 3/1, 2021 at 14:26 Comment(0)
D
7

An example with multiple path and multiple methods for one resource

async fn index() -> impl Responder {
  HttpResponse::Ok().body("Hello world!")
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
  HttpServer::new(move || {
    App::new()
        .service(
            actix_web::web::resource(vec!["/", "/index"])
                .route(actix_web::web::get().to(index))
                .route(actix_web::web::post().to(index))
            )
  })
  .bind("127.0.0.1:8080")?
  .run()
  .await
}
Dermatoplasty answered 8/1, 2021 at 9:24 Comment(0)
A
1

It is possible to use multiple macros for a single function since mid 2022 (see this github issue)

Example:

#[routes]
#[get("/")] #[head("/")]
#[get("/index")] #[head("/index")]
#[get("/index.html")] #[head("/index.html")]
async fn index() -> impl Responder {
    // HttpResponse::Ok().finish()
}

// In main():
App::new().service(index)
Ardolino answered 1/2 at 18:59 Comment(0)
P
-3

I assume you are using actix-web: 2.0.0 with actix-rt: 1.0.0 and this handler you are passing to App.service method like below

HttpServer::new(move || {
            App::new()
                .wrap(middleware::Logger::default())
                .service(index)
        })
        .bind(("127.0.0.1", self.port))?
        .workers(8)
        .run()
        .await

then you will need to write handler like this ->

/// Returns a UUID4.
#[get("/uuid")]
async fn uuid_v4(req: HttpRequest) -> Result<web::Json<IndexResponse>> {
    let uuid_header = req
        .headers()
        .get("uuid")
        .and_then(|v| v.to_str().ok())
        .unwrap_or_else(|| "some-id");
    //curl -H "uuid: username" localhost:8080

    println!("make use of {}", uuid_header);
    Ok(web::Json(Uuid {
        uuid: uuid::Uuid::new_v4(),
    }))
}
Possessive answered 9/8, 2020 at 10:35 Comment(1)
I do not understand how this supports anything else apart from the GET method.Warram

© 2022 - 2024 — McMap. All rights reserved.