From d878eede1897456c15d257aca8e52d588e685198 Mon Sep 17 00:00:00 2001 From: Altair-Bueno <67512202+Altair-Bueno@users.noreply.github.com> Date: Mon, 15 Aug 2022 18:51:58 +0200 Subject: [PATCH] examples: Replaced custom `Json` extractor with `WithRejection` --- examples/customize-extractor-error/Cargo.toml | 1 + .../customize-extractor-error/src/main.rs | 95 ++++++++++--------- 2 files changed, 50 insertions(+), 46 deletions(-) diff --git a/examples/customize-extractor-error/Cargo.toml b/examples/customize-extractor-error/Cargo.toml index 95f9908050..d632d15a0b 100644 --- a/examples/customize-extractor-error/Cargo.toml +++ b/examples/customize-extractor-error/Cargo.toml @@ -6,6 +6,7 @@ publish = false [dependencies] axum = { path = "../../axum" } +axum-extra = { path = "../../axum-extra" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" tokio = { version = "1.0", features = ["full"] } diff --git a/examples/customize-extractor-error/src/main.rs b/examples/customize-extractor-error/src/main.rs index bc0973b4c6..487149741b 100644 --- a/examples/customize-extractor-error/src/main.rs +++ b/examples/customize-extractor-error/src/main.rs @@ -5,15 +5,15 @@ //! ``` use axum::{ - async_trait, - extract::{rejection::JsonRejection, FromRequest, RequestParts}, + extract::rejection::JsonRejection, http::StatusCode, - routing::post, - BoxError, Router, + response::IntoResponse, + routing::post, Json, Router, }; -use serde::{de::DeserializeOwned, Deserialize}; -use serde_json::{json, Value}; -use std::{borrow::Cow, net::SocketAddr}; +use axum_extra::extract::WithRejection; +use serde::Deserialize; +use serde_json::json; +use std::net::SocketAddr; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; #[tokio::main] @@ -38,7 +38,12 @@ async fn main() { .unwrap(); } -async fn handler(Json(user): Json) { +async fn handler( + // `WithRejection` will extract `Json` from the request. If the + // extraction fails, a `MyRejection` will be created from `JsonResponse` and + // returned to the client + WithRejection(Json(user), _): WithRejection, MyRejection> +) { dbg!(&user); } @@ -49,46 +54,44 @@ struct User { username: String, } -// We define our own `Json` extractor that customizes the error from `axum::Json` -struct Json(T); - -#[async_trait] -impl FromRequest for Json -where - // these trait bounds are copied from `impl FromRequest for axum::Json` - T: DeserializeOwned, - B: axum::body::HttpBody + Send, - B::Data: Send, - B::Error: Into, -{ - type Rejection = (StatusCode, axum::Json); +// Define your own custom rejection +#[derive(Debug)] +struct MyRejection { + body: String, + status: StatusCode, +} - async fn from_request(req: &mut RequestParts) -> Result { - match axum::Json::::from_request(req).await { - Ok(value) => Ok(Self(value.0)), - Err(rejection) => { - // convert the error from `axum::Json` into whatever we want - let (status, body): (_, Cow<'_, str>) = match rejection { - JsonRejection::JsonDataError(err) => ( - StatusCode::BAD_REQUEST, - format!("Invalid JSON request: {}", err).into(), - ), - JsonRejection::MissingJsonContentType(err) => { - (StatusCode::BAD_REQUEST, err.to_string().into()) - } - err => ( - StatusCode::INTERNAL_SERVER_ERROR, - format!("Unknown internal error: {}", err).into(), - ), - }; +// `IntoResponse` is required for your custom rejection type +impl IntoResponse for MyRejection { + fn into_response(self) -> axum::response::Response { + let Self { body, status } = self; + ( + status, + // we use `axum::Json` here to generate a JSON response + // body but you can use whatever response you want + axum::Json(json!({ "error": body })), + ) + .into_response() + } +} - Err(( - status, - // we use `axum::Json` here to generate a JSON response - // body but you can use whatever response you want - axum::Json(json!({ "error": body })), - )) +// Implement `From` for any Rejection type you want +impl From for MyRejection { + fn from(rejection: JsonRejection) -> Self { + // convert the error from `axum::Json` into whatever we want + let (status, body) = match rejection { + JsonRejection::JsonDataError(err) => ( + StatusCode::BAD_REQUEST, + format!("Invalid JSON request: {}", err), + ), + JsonRejection::MissingJsonContentType(err) => { + (StatusCode::BAD_REQUEST, err.to_string()) } - } + err => ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("Unknown internal error: {}", err), + ), + }; + Self { body, status } } }