axum-bindform 0.1.0

Bind XML, JSON, URL-encoded or query-string form data in Axum.
Documentation
use axum::{body::HttpBody, extract::FromRequest, http::Request, BoxError};
use serde::de::DeserializeOwned;

use crate::{BindError, BindResult};

pub(crate) async fn bind_request<S, B, T>(req: Request<B>, state: &S) -> BindResult<T>
where
    T: DeserializeOwned,
    B: HttpBody + Send + 'static,
    B::Data: Send,
    B::Error: Into<BoxError>,
    S: Send + Sync,
{
    let content_type = req
        .headers()
        .get("content-type")
        .and_then(|v| v.to_str().ok())
        .unwrap_or("");

    #[cfg(feature = "urlencoded")]
    if content_type.is_empty() {
        let query = req.uri().query().unwrap_or("");
        let deserialized =
            serde_urlencoded::from_str::<T>(query).map_err(BindError::UrlEncodedError)?;

        return Ok(deserialized);
    }

    let mime = match content_type.parse::<mime::Mime>() {
        Ok(mime) => mime,
        Err(_) => return Err(BindError::InvalidMimeType),
    };

    #[cfg(feature = "json")]
    if mime.type_() == mime::APPLICATION
        && (mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON))
    {
        let bytes = axum::body::Bytes::from_request(req, state)
            .await
            .map_err(BindError::BodyReadError)?;

        let deserialized = serde_json::from_slice(&bytes).map_err(BindError::JsonError)?;

        return Ok(deserialized);
    }

    #[cfg(feature = "urlencoded")]
    if mime.type_() == mime::APPLICATION
        && (mime.subtype() == mime::WWW_FORM_URLENCODED
            || mime.suffix() == Some(mime::WWW_FORM_URLENCODED))
    {
        let bytes = axum::body::Bytes::from_request(req, state)
            .await
            .map_err(BindError::BodyReadError)?;

        let query = std::str::from_utf8(&bytes).unwrap_or("");
        let deserialized =
            serde_urlencoded::from_str::<T>(query).map_err(BindError::UrlEncodedError)?;

        return Ok(deserialized);
    }

    #[cfg(feature = "xml")]
    if mime.type_() == mime::APPLICATION
        && (mime.subtype() == mime::XML || mime.suffix() == Some(mime::XML))
    {
        let bytes = axum::body::Bytes::from_request(req, state)
            .await
            .map_err(BindError::BodyReadError)?;

        let deserialized = serde_xml_rs::from_reader(&bytes[..]).map_err(BindError::XmlError)?;

        return Ok(deserialized);
    }

    Err(BindError::InvalidMimeType)
}