Form data and multipart in axum

i have some code that's like this

    struct F {
        x: String,
    }

    async fn form_and_multipart(
        Form(f): Form<F>,
        m: Multipart,
    ) -> impl IntoResponse {
        Html("")
    }

but this doesn't work

error[E0277]: the trait bound `fn(Form<F>, Multipart) -> impl std::future::Future<Output = impl IntoResponse> {form_and_multipart}: Handler<_, _>` is not satisfied
   --> src/act.rs:319:49
    |
319 | ...).route("/", post(form_and_multipart));
    |                 ---- ^^^^^^^^^^^^^^^^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Form<...>, ...) -> ... {form_and_multipart}`
    |                 |
    |                 required by a bound introduced by this call
    |
    = note: the full name for the type has been written to '/home/skye/src/pulver/target/debug/deps/pulver-80ef0135f62c1e85.long-type-5275344219598188898.txt'
    = note: consider using `--verbose` to print the full type name to the console
    = note: Consider using `#[axum::debug_handler]` to improve the error message
    = help: the following other types implement trait `Handler<T, S>`:
              `Layered<L, H, T, S>` implements `Handler<T, S>`
              `MethodRouter<S>` implements `Handler<(), S>`
note: required by a bound in `post`
   --> /home/skye/.cargo/registry/src/index.crates.io-6f17d22bba15001f/axum-0.7.5/src/routing/method_routing.rs:389:1
    |
389 | top_level_handler_fn!(post, POST);
    | ^^^^^^^^^^^^^^^^^^^^^^----^^^^^^^
    | |                     |
    | |                     required by a bound in this function
    | required by this bound in `post`
    = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0277`.
warning: `pulver` (lib test) generated 3 warnings (1 duplicate)
error: could not compile `pulver` (lib test) due to 1 previous error; 3 warnings emitted

and i don't know why. is what im trying to do not possible?

I believe because Form<_> doesn't implement FromRequestParts<_>, just from reading the implementation bounds.

But I don't know axum enough to offer a suggestion forward.

Both Form<_> and Multipart implement FromRequest rather than FromRequestParts. FromRequest consumes the request body and as such can only be run once. Axum enforces this by requiring that if a FromRequest extractor is used, it must the last argument of the function signature.

Instead of using both, use only Multipart and the Multipart::next_field method to access the data. You won't have all the nice deserialisation machinery that form provides, but it will be workable.

2 Likes

The form extractor uses serde_urlencoded under the hood, OP can just use it manually on the fields of the multipart body.

Also I wouldn't call anything related to x-www-form-urlencoded "nice" :smile:

When your function does not implement Handler trait, then compiler errors can be pretty atrocious (compared to Rust standards). You can use axum::debug_handler macro on your handlers, which can produce better explanation why given function does not implement Handler.

Apologies, by form, I meant Form<T>. :wink:

1 Like

can you point me in the direction of how id do that? never worked with serde before

Sure, something like this should work:

use serde::Deserialize;

#[derive(Deserialize)]
struct F {
    x: String,
}

fn main() {
    let urlencoded = "x=hello%20world!";

    let f: F = serde_urlencoded::from_str(urlencoded).unwrap();

    assert_eq!(f.x, "hello world!".to_owned());
}

Playground.

You can use Field::text or Field::bytes to extract the multipart field's body and pass it to serde_urlencoded::from_str or serde_urlencoded::from_bytes respectively.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.