mirror of
https://github.com/tokio-rs/axum.git
synced 2026-08-29 00:00:18 +02:00
Add #[derive(FromRequestParts)] (#1305)
* Add missing leading double colon * Separate handling of last element in FromRequest derive * FromRequestParts derive * fix it and add lots of tests * docs * changelog * Update axum-macros/src/lib.rs Co-authored-by: Jonas Platte <[email protected]> Co-authored-by: David Pedersen <[email protected]>
This commit is contained in:
co-authored by
David Pedersen
parent
db08419a3b
commit
7705ef6661
@@ -0,0 +1,15 @@
|
||||
use axum::{extract::FromRequestParts, response::Response};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor {
|
||||
body: String,
|
||||
}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: FromRequestParts<(), Rejection = Response>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,16 @@
|
||||
error[E0277]: the trait bound `String: FromRequestParts<S>` is not satisfied
|
||||
--> tests/from_request/fail/parts_extracting_body.rs:6:11
|
||||
|
|
||||
6 | body: String,
|
||||
| ^^^^^^ the trait `FromRequestParts<S>` is not implemented for `String`
|
||||
|
|
||||
= help: the following other types implement trait `FromRequestParts<S>`:
|
||||
<() as FromRequestParts<S>>
|
||||
<(T1, T2) as FromRequestParts<S>>
|
||||
<(T1, T2, T3) as FromRequestParts<S>>
|
||||
<(T1, T2, T3, T4) as FromRequestParts<S>>
|
||||
<(T1, T2, T3, T4, T5) as FromRequestParts<S>>
|
||||
<(T1, T2, T3, T4, T5, T6) as FromRequestParts<S>>
|
||||
<(T1, T2, T3, T4, T5, T6, T7) as FromRequestParts<S>>
|
||||
<(T1, T2, T3, T4, T5, T6, T7, T8) as FromRequestParts<S>>
|
||||
and 27 others
|
||||
@@ -0,0 +1,21 @@
|
||||
use axum::{
|
||||
extract::{FromRequestParts, Extension},
|
||||
response::Response,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(Clone, FromRequestParts)]
|
||||
#[from_request(via(Extension))]
|
||||
struct Extractor {
|
||||
one: i32,
|
||||
two: String,
|
||||
three: bool,
|
||||
}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: FromRequestParts<(), Rejection = Response>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor {}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor();
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
use axum::{body::Body, routing::get, Extension, Router};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts, Clone)]
|
||||
#[from_request(via(Extension))]
|
||||
enum Extractor {}
|
||||
|
||||
async fn foo(_: Extractor) {}
|
||||
|
||||
fn main() {
|
||||
Router::<(), Body>::new().route("/", get(foo));
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
use axum::{
|
||||
extract::{rejection::TypedHeaderRejection, FromRequestParts, TypedHeader},
|
||||
headers::{self, UserAgent},
|
||||
response::Response,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor {
|
||||
uri: axum::http::Uri,
|
||||
user_agent: TypedHeader<UserAgent>,
|
||||
content_type: TypedHeader<headers::ContentType>,
|
||||
etag: Option<TypedHeader<headers::ETag>>,
|
||||
host: Result<TypedHeader<headers::Host>, TypedHeaderRejection>,
|
||||
}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: FromRequestParts<(), Rejection = Response>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,34 @@
|
||||
use axum::{
|
||||
response::Response,
|
||||
extract::{
|
||||
rejection::TypedHeaderRejection,
|
||||
Extension, FromRequestParts, TypedHeader,
|
||||
},
|
||||
headers::{self, UserAgent},
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor {
|
||||
#[from_request(via(Extension))]
|
||||
state: State,
|
||||
#[from_request(via(TypedHeader))]
|
||||
user_agent: UserAgent,
|
||||
#[from_request(via(TypedHeader))]
|
||||
content_type: headers::ContentType,
|
||||
#[from_request(via(TypedHeader))]
|
||||
etag: Option<headers::ETag>,
|
||||
#[from_request(via(TypedHeader))]
|
||||
host: Result<headers::Host, TypedHeaderRejection>,
|
||||
}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: FromRequestParts<(), Rejection = Response>,
|
||||
{
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct State;
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,39 @@
|
||||
use axum::{
|
||||
extract::rejection::JsonRejection,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_macros::FromRequest;
|
||||
use std::collections::HashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
fn main() {
|
||||
let _: Router = Router::new().route("/", get(handler).post(handler_result));
|
||||
}
|
||||
|
||||
async fn handler(_: MyJson) {}
|
||||
|
||||
async fn handler_result(_: Result<MyJson, MyJsonRejection>) {}
|
||||
|
||||
#[derive(FromRequest, Deserialize)]
|
||||
#[from_request(
|
||||
via(axum::extract::Json),
|
||||
rejection(MyJsonRejection),
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
struct MyJson(HashMap<String, String>);
|
||||
|
||||
struct MyJsonRejection {}
|
||||
|
||||
impl From<JsonRejection> for MyJsonRejection {
|
||||
fn from(_: JsonRejection) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MyJsonRejection {
|
||||
fn into_response(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
use axum::{
|
||||
extract::rejection::QueryRejection,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
use std::collections::HashMap;
|
||||
use serde::Deserialize;
|
||||
|
||||
fn main() {
|
||||
let _: Router = Router::new().route("/", get(handler).post(handler_result));
|
||||
}
|
||||
|
||||
async fn handler(_: MyQuery) {}
|
||||
|
||||
async fn handler_result(_: Result<MyQuery, MyQueryRejection>) {}
|
||||
|
||||
#[derive(FromRequestParts, Deserialize)]
|
||||
#[from_request(
|
||||
via(axum::extract::Query),
|
||||
rejection(MyQueryRejection),
|
||||
)]
|
||||
#[serde(transparent)]
|
||||
struct MyQuery(HashMap<String, String>);
|
||||
|
||||
struct MyQueryRejection {}
|
||||
|
||||
impl From<QueryRejection> for MyQueryRejection {
|
||||
fn from(_: QueryRejection) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MyQueryRejection {
|
||||
fn into_response(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use axum::{
|
||||
async_trait,
|
||||
extract::{rejection::ExtensionRejection, FromRequestParts},
|
||||
http::{request::Parts, StatusCode},
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Extension, Router,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
fn main() {
|
||||
let _: Router = Router::new().route("/", get(handler).post(handler_result));
|
||||
}
|
||||
|
||||
async fn handler(_: MyExtractor) {}
|
||||
|
||||
async fn handler_result(_: Result<MyExtractor, MyRejection>) {}
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
#[from_request(rejection(MyRejection))]
|
||||
struct MyExtractor {
|
||||
one: Extension<String>,
|
||||
#[from_request(via(Extension))]
|
||||
two: String,
|
||||
three: OtherExtractor,
|
||||
}
|
||||
|
||||
struct OtherExtractor;
|
||||
|
||||
#[async_trait]
|
||||
impl<S> FromRequestParts<S> for OtherExtractor
|
||||
where
|
||||
S: Send + Sync,
|
||||
{
|
||||
// this rejection doesn't implement `Display` and `Error`
|
||||
type Rejection = (StatusCode, String);
|
||||
|
||||
async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct MyRejection {}
|
||||
|
||||
impl From<ExtensionRejection> for MyRejection {
|
||||
fn from(_: ExtensionRejection) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(StatusCode, String)> for MyRejection {
|
||||
fn from(_: (StatusCode, String)) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MyRejection {
|
||||
fn into_response(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
use axum::{
|
||||
extract::rejection::ExtensionRejection,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
fn main() {
|
||||
let _: Router = Router::new().route("/", get(handler).post(handler_result));
|
||||
}
|
||||
|
||||
async fn handler(_: MyExtractor) {}
|
||||
|
||||
async fn handler_result(_: Result<MyExtractor, MyRejection>) {}
|
||||
|
||||
#[derive(FromRequestParts, Clone)]
|
||||
#[from_request(via(axum::Extension), rejection(MyRejection))]
|
||||
enum MyExtractor {}
|
||||
|
||||
struct MyRejection {}
|
||||
|
||||
impl From<ExtensionRejection> for MyRejection {
|
||||
fn from(_: ExtensionRejection) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MyRejection {
|
||||
fn into_response(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
use axum::{
|
||||
extract::rejection::QueryRejection,
|
||||
response::{IntoResponse, Response},
|
||||
routing::get,
|
||||
Router,
|
||||
};
|
||||
use axum_macros::FromRequestParts;
|
||||
use serde::Deserialize;
|
||||
|
||||
fn main() {
|
||||
let _: Router = Router::new().route("/", get(handler).post(handler_result));
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Payload {}
|
||||
|
||||
async fn handler(_: MyQuery<Payload>) {}
|
||||
|
||||
async fn handler_result(_: Result<MyQuery<Payload>, MyQueryRejection>) {}
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
#[from_request(
|
||||
via(axum::extract::Query),
|
||||
rejection(MyQueryRejection),
|
||||
)]
|
||||
struct MyQuery<T>(T);
|
||||
|
||||
struct MyQueryRejection {}
|
||||
|
||||
impl From<QueryRejection> for MyQueryRejection {
|
||||
fn from(_: QueryRejection) -> Self {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoResponse for MyQueryRejection {
|
||||
fn into_response(self) -> Response {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor(axum::http::HeaderMap, axum::http::Method);
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<()>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,20 @@
|
||||
use axum::extract::Query;
|
||||
use axum_macros::FromRequestParts;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor(
|
||||
Query<Payload>,
|
||||
axum::extract::Path<Payload>,
|
||||
);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Payload {}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<()>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,21 @@
|
||||
use axum::extract::Query;
|
||||
use axum::response::Response;
|
||||
use axum_macros::FromRequestParts;
|
||||
use serde::Deserialize;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor(
|
||||
#[from_request(via(Query))] Payload,
|
||||
#[from_request(via(axum::extract::Path))] Payload,
|
||||
);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Payload {}
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<(), Rejection = Response>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,16 @@
|
||||
use axum::Extension;
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor(#[from_request(via(Extension))] State);
|
||||
|
||||
#[derive(Clone)]
|
||||
struct State;
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<()>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
@@ -0,0 +1,12 @@
|
||||
use axum_macros::FromRequestParts;
|
||||
|
||||
#[derive(FromRequestParts)]
|
||||
struct Extractor;
|
||||
|
||||
fn assert_from_request()
|
||||
where
|
||||
Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>,
|
||||
{
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
Reference in New Issue
Block a user