0

In order to support application/json and multipart/form-data on the same URL, I would like to check the "Content-Type" header and choose a proper Data<T> type to hand in to the .data function of App::new.

If I uncomment the .guard line, then curl -X POST -H "Content-Type: multipart/form-data" -F files=\"qqq\" localhost:8080/upload is dropped. But without the .guard line all works as it was supposed. What is wrong?

HttpServer::new(move || {
    App::new()
        .service(resource("/upload")
     // .guard(actix_web::guard::Header("Content-Type", "multipart/form-data"))
        .data(form.clone())
        .route(post()
        .to(upload_multipart)
        )   
    )
})

How to join them properly in one instance of App?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
unegare
  • 2,197
  • 1
  • 11
  • 25

1 Answers1

2

Currently, actix-web 1.0.3 does not support multipart/form-data, but you can use actix_multipart. Since the focus is deserializing same data with different content-types, I've simplified to using application/x-www-form-urlencoded.

To support two different content-types, nest web::resource and add guards to each of the handlers:

web::resource("/")
    .route(
        web::post()
            .guard(guard::Header(
                "content-type",
                "application/x-www-form-urlencoded",
            ))
            .to(form_handler),
    )
    .route(
        web::post()
            .guard(guard::Header("content-type", "application/json"))
            .to(json_handler),
    ),

Create handlers that takes deserialized data, and send the data to a common handler:

fn form_handler(user: web::Form<User>) -> String {
    handler(user.into_inner())
}

fn json_handler(user: web::Json<User>) -> String {
    handler(user.into_inner())
}

fn handler(user: User) -> String {
    format!("Got username: {}", user.username)
}

Result:

$ curl -d 'username=adsf' localhost:8000
Got username: asdf⏎
$ curl -d '{"username": "asdf"}' localhost:8000
Parse error⏎
$ curl -d '{"username": "asdf"}' -H 'content-type: application/json' localhost:8000
Got username: asdf⏎

To create your own deserializer, implement the FromRequest trait.

arve0
  • 3,424
  • 26
  • 33
  • It is not what I want, because it does not solve problem of changing the parser of the body handed in `.data` method – unegare Jul 09 '19 at 17:20
  • You want to deserialize the body, with two different deserializers, depending on content-type? – arve0 Jul 09 '19 at 18:54
  • Exactly. two different deserializers depending on content-type – unegare Jul 10 '19 at 10:31
  • I believe you should use two handlers then, each having it’s own guard. One handler using the [json extractor](https://actix.rs/docs/extractors/#json), the other using the [form extractor](https://actix.rs/docs/extractors/#form). The handlers can be nested inside a `web::resource`. – arve0 Jul 10 '19 at 16:18
  • I've updated the answer to reflect my last comment. – arve0 Jul 11 '19 at 21:07