mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-20 00:00:11 +02:00
examples(error-handling): custom_extractor
This commit is contained in:
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
axum = { path = "../../axum" }
|
||||
axum = { path = "../../axum", features=["matched-path"] }
|
||||
axum-extra = { path = "../../axum-extra" }
|
||||
chrono = { version = "0.4", features = ["serde"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
use axum::extract::MatchedPath;
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::{rejection::JsonRejection, FromRequest, RequestParts},
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
BoxError,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::{json, Value};
|
||||
|
||||
pub async fn handler(Json(value): Json<Value>) -> impl IntoResponse {
|
||||
Json(dbg!(value));
|
||||
}
|
||||
|
||||
// We define our own `Json` extractor that customizes the error from `axum::Json`
|
||||
pub struct Json<T>(T);
|
||||
|
||||
#[async_trait]
|
||||
impl<S, B, T> FromRequest<S, B> for Json<T>
|
||||
where
|
||||
S: Send + Sync,
|
||||
// these trait bounds are copied from `impl FromRequest for axum::Json`
|
||||
T: DeserializeOwned,
|
||||
B: axum::body::HttpBody + Send,
|
||||
B::Data: Send,
|
||||
B::Error: Into<BoxError>,
|
||||
{
|
||||
type Rejection = (StatusCode, axum::Json<Value>);
|
||||
|
||||
async fn from_request(req: &mut RequestParts<S, B>) -> Result<Self, Self::Rejection> {
|
||||
match axum::Json::<T>::from_request(req).await {
|
||||
Ok(value) => Ok(Self(value.0)),
|
||||
Err(rejection) => {
|
||||
// convert the error from `axum::Json` into whatever we want
|
||||
let path = req
|
||||
.extensions()
|
||||
.get::<MatchedPath>()
|
||||
.map(|x| x.as_str().to_owned());
|
||||
|
||||
let payload = json!({
|
||||
"message": rejection.to_string(),
|
||||
"timestamp": Utc::now(),
|
||||
"origin": "custom_extractor",
|
||||
"path": path,
|
||||
});
|
||||
|
||||
let code = match rejection {
|
||||
JsonRejection::JsonDataError(_) | JsonRejection::MissingJsonContentType(_) => {
|
||||
StatusCode::BAD_REQUEST
|
||||
}
|
||||
_ => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
};
|
||||
Err((code, axum::Json(payload)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,23 @@
|
||||
mod with_rejection;
|
||||
mod custom_extractor;
|
||||
|
||||
use axum::{routing::get, Router, Server};
|
||||
use std::net::SocketAddr;
|
||||
use axum::{Server, Router, routing::get};
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::registry()
|
||||
.with(tracing_subscriber::EnvFilter::new(
|
||||
std::env::var("RUST_LOG")
|
||||
.unwrap_or_else(|_| "error_handling=debug".into()),
|
||||
std::env::var("RUST_LOG").unwrap_or_else(|_| "error_handling=debug".into()),
|
||||
))
|
||||
.with(tracing_subscriber::fmt::layer())
|
||||
.init();
|
||||
|
||||
// Build our application with some routes
|
||||
let app = Router::new()
|
||||
.route("/withRejection", get(with_rejection::handler));
|
||||
.route("/withRejection", get(with_rejection::handler))
|
||||
.route("/customExtractor", get(custom_extractor::handler));
|
||||
|
||||
// Run our application
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
@@ -27,4 +26,4 @@ async fn main() {
|
||||
.serve(app.into_make_service())
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user