Validating Json Request in axum

An extractor I wrote to validate json in axum

Published on
2 min, 367 words

Categories: post

I have been playing around with axum, and it has been quite a fun web framework. While using it, I came across what is a relatively common use case of validating the request JSON. However, I could not find any extractor for this. Thus I decided to write my own and share it with everyone. While I haven't put it in a crate, feel free to use the code as you wish.

Axum Extractors

An extractor allows us to pick apart the incoming request to get the parts our HTTP handler needs. More about extractors can be found here. There are two important traits when talking about extractors:

  1. FromRequestParts: This is used if the extractor does not need access to the request body.
  2. FromRequest: This is used if the extractor does need access to the request body. (we will be using this)

Implementation

I will use validator crate for the actual validation.

Here is the code for our validated JSON extractor:

use axum::{async_trait, extract::FromRequest, Json, RequestExt};
use hyper::{Request, StatusCode};
use validator::Validate;

pub struct ValidatedJson<J>(pub J);

#[async_trait]
impl<S, B, J> FromRequest<S, B> for ValidatedJson<J>
where
    B: Send + 'static,
    S: Send + Sync,
    J: Validate + 'static,
    Json<J>: FromRequest<(), B>,
{
    type Rejection = (StatusCode, &'static str);

    async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
        let Json(data) = req
            .extract::<Json<J>, _>()
            .await
            .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
        data.validate()
            .map_err(|_| (StatusCode::BAD_REQUEST, "Invalid JSON body"))?;
        Ok(Self(data))
    }
}

I am using (StatusCode, &'static str) for error response since all the responses in my server are of this type. Feel free to use whatever you prefer.

It is important to note that extractors can use other extractors themselves. So we do not need to replicate the Json extractor.

Conclusion

As you can see, writing a custom extractor is relatively straightforward, especially when compared to writing a tower middleware.