mattgathu
mattgathu

Reputation: 1147

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

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

Upvotes: 7

Views: 3980

Answers (4)

camelCase1492
camelCase1492

Reputation: 672

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)

Upvotes: 1

Jbs Nice
Jbs Nice

Reputation: 81

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
}

Upvotes: 8

tbounsiar
tbounsiar

Reputation: 309

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
}

Upvotes: 7

STEEL
STEEL

Reputation: 10067

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(),
    }))
}

Upvotes: -3

Related Questions