-
Notifications
You must be signed in to change notification settings - Fork 967
/
main.rs
89 lines (77 loc) · 2.52 KB
/
main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Provides a RESTful web server managing some Todos.
//!
//! Run with
//!
//! ```not_rust
//! cd examples && cargo run -p example-parse-body-based-on-content-type
//! ```
use axum::{
async_trait,
extract::FromRequest,
http::{header::CONTENT_TYPE, Request, StatusCode},
response::{IntoResponse, Response},
routing::post,
Form, Json, RequestExt, Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
"example_parse_body_based_on_content_type=debug,tower_http=debug".into()
}),
)
.with(tracing_subscriber::fmt::layer())
.init();
let app = Router::new().route("/", post(handler));
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
tracing::debug!("listening on {}", addr);
axum::Server::bind(&addr)
.serve(app.into_make_service())
.await
.unwrap();
}
#[derive(Serialize, Deserialize)]
struct Payload {
foo: String,
}
async fn handler(payload: JsonOrForm<Payload>) -> Response {
match payload {
JsonOrForm::Json(payload) => Json(payload).into_response(),
JsonOrForm::Form(payload) => Form(payload).into_response(),
}
}
enum JsonOrForm<T, K = T> {
Json(T),
Form(K),
}
#[async_trait]
impl<S, B, T, U> FromRequest<S, B> for JsonOrForm<T, U>
where
B: Send + 'static,
S: Send + Sync,
Json<T>: FromRequest<(), B>,
Form<U>: FromRequest<(), B>,
T: 'static,
U: 'static,
{
type Rejection = Response;
async fn from_request(req: Request<B>, _state: &S) -> Result<Self, Self::Rejection> {
let content_type_header = req.headers().get(CONTENT_TYPE);
let content_type = content_type_header.and_then(|value| value.to_str().ok());
if let Some(content_type) = content_type {
if content_type.starts_with("application/json") {
let Json(payload) = req.extract().await.map_err(IntoResponse::into_response)?;
return Ok(Self::Json(payload));
}
if content_type.starts_with("application/x-www-form-urlencoded") {
let Form(payload) = req.extract().await.map_err(IntoResponse::into_response)?;
return Ok(Self::Form(payload));
}
}
Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response())
}
}