axum-extra: Make axum optional dependency (#3485)

This commit is contained in:
tottoto 2025-09-21 05:50:56 +09:00 committed by GitHub
parent 40e9d7b22a
commit d184798937
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 90 additions and 46 deletions

View File

@ -9,8 +9,16 @@ and this project adheres to [Semantic Versioning].
introduced as an implicit feature through an optional dependency which was no introduced as an implicit feature through an optional dependency which was no
longer being used ([#3298]) longer being used ([#3298])
- **breaking:** `option_layer` now maps the `Response` body type to `axum::body::Body` ([#3469]) - **breaking:** `option_layer` now maps the `Response` body type to `axum::body::Body` ([#3469])
- **breaking:** Some new features are added which need to be opted in ([#3485]).
- `Cached` extractor requires `cached` feature.
- The handler utilities require `handler` feature.
- The middleware utilities require `middleware` feature.
- `OptionalPath` extractor requires `optional-path` feature.
- The routing utilities require `routing` feature.
- `WithRejection` extractor requires `with-rejection` feature.
[#3298]: https://github.com/tokio-rs/axum/pull/3298 [#3298]: https://github.com/tokio-rs/axum/pull/3298
[#3485]: https://github.com/tokio-rs/axum/pull/3485
# 0.11.0 # 0.11.0

View File

@ -40,6 +40,7 @@ allowed = [
default = ["tracing"] default = ["tracing"]
async-read-body = ["dep:tokio-util", "tokio-util?/io", "dep:tokio"] async-read-body = ["dep:tokio-util", "tokio-util?/io", "dep:tokio"]
cached = ["dep:axum"]
file-stream = [ file-stream = [
"dep:tokio-util", "dep:tokio-util",
"tokio-util?/io", "tokio-util?/io",
@ -54,7 +55,13 @@ cookie-private = ["cookie", "cookie?/private"]
cookie-signed = ["cookie", "cookie?/signed"] cookie-signed = ["cookie", "cookie?/signed"]
cookie-key-expansion = ["cookie", "cookie?/key-expansion"] cookie-key-expansion = ["cookie", "cookie?/key-expansion"]
erased-json = ["dep:serde_json", "dep:typed-json"] erased-json = ["dep:serde_json", "dep:typed-json"]
form = ["dep:form_urlencoded", "dep:serde_html_form", "dep:serde_path_to_error"] form = [
"dep:axum",
"dep:form_urlencoded",
"dep:serde_html_form",
"dep:serde_path_to_error",
]
handler = ["dep:axum"]
json-deserializer = ["dep:serde_json", "dep:serde_path_to_error"] json-deserializer = ["dep:serde_json", "dep:serde_path_to_error"]
json-lines = [ json-lines = [
"dep:serde_json", "dep:serde_json",
@ -64,8 +71,11 @@ json-lines = [
"tokio-stream?/io-util", "tokio-stream?/io-util",
"dep:tokio", "dep:tokio",
] ]
middleware = ["dep:axum"]
multipart = ["dep:multer", "dep:fastrand"] multipart = ["dep:multer", "dep:fastrand"]
optional-path = ["dep:axum"]
protobuf = ["dep:prost"] protobuf = ["dep:prost"]
routing = ["axum/original-uri"]
scheme = [] scheme = []
query = ["dep:form_urlencoded", "dep:serde_html_form", "dep:serde_path_to_error"] query = ["dep:form_urlencoded", "dep:serde_html_form", "dep:serde_path_to_error"]
tracing = ["axum-core/tracing", "axum/tracing", "dep:tracing"] tracing = ["axum-core/tracing", "axum/tracing", "dep:tracing"]
@ -76,13 +86,13 @@ typed-routing = [
"dep:serde_html_form", "dep:serde_html_form",
"dep:form_urlencoded", "dep:form_urlencoded",
] ]
with-rejection = ["dep:axum"]
# Enabled by docs.rs because it uses all-features # Enabled by docs.rs because it uses all-features
# Enables upstream things linked to in docs # Enables upstream things linked to in docs
__private_docs = ["axum/json", "dep:serde"] __private_docs = ["axum/json", "dep:serde"]
[dependencies] [dependencies]
axum = { path = "../axum", version = "0.8.4", default-features = false, features = ["original-uri"] }
axum-core = { path = "../axum-core", version = "0.5.2" } axum-core = { path = "../axum-core", version = "0.5.2" }
bytes = "1.1.0" bytes = "1.1.0"
futures-core = "0.3" futures-core = "0.3"
@ -99,6 +109,7 @@ tower-layer = "0.3"
tower-service = "0.3" tower-service = "0.3"
# optional dependencies # optional dependencies
axum = { path = "../axum", version = "0.8.4", default-features = false, optional = true }
axum-macros = { path = "../axum-macros", version = "0.5.0", optional = true } axum-macros = { path = "../axum-macros", version = "0.5.0", optional = true }
cookie = { package = "cookie", version = "0.18.0", features = ["percent-encode"], optional = true } cookie = { package = "cookie", version = "0.18.0", features = ["percent-encode"], optional = true }
fastrand = { version = "2.1.0", optional = true } fastrand = { version = "2.1.0", optional = true }

View File

@ -1,8 +1,10 @@
use axum::{ use axum_core::{
body::{Body, Bytes, HttpBody}, body::Body,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
Error, Error,
}; };
use bytes::Bytes;
use http_body::Body as HttpBody;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use std::{ use std::{
pin::Pin, pin::Pin,

View File

@ -92,7 +92,7 @@
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use axum::{ use axum_core::{
extract::FromRequestParts, extract::FromRequestParts,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };

View File

@ -2,7 +2,7 @@
//! //!
//! See [`CookieJar`], [`SignedCookieJar`], and [`PrivateCookieJar`] for more details. //! See [`CookieJar`], [`SignedCookieJar`], and [`PrivateCookieJar`] for more details.
use axum::{ use axum_core::{
extract::FromRequestParts, extract::FromRequestParts,
response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, response::{IntoResponse, IntoResponseParts, Response, ResponseParts},
}; };

View File

@ -1,5 +1,5 @@
use super::{cookies_from_request, set_cookies, Cookie, Key}; use super::{cookies_from_request, set_cookies, Cookie, Key};
use axum::{ use axum_core::{
extract::{FromRef, FromRequestParts}, extract::{FromRef, FromRequestParts},
response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, response::{IntoResponse, IntoResponseParts, Response, ResponseParts},
}; };

View File

@ -1,5 +1,5 @@
use super::{cookies_from_request, set_cookies}; use super::{cookies_from_request, set_cookies};
use axum::{ use axum_core::{
extract::{FromRef, FromRequestParts}, extract::{FromRef, FromRequestParts},
response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, response::{IntoResponse, IntoResponseParts, Response, ResponseParts},
}; };

View File

@ -1,5 +1,5 @@
use super::rejection::{FailedToResolveHost, HostRejection}; use super::rejection::{FailedToResolveHost, HostRejection};
use axum::{ use axum_core::{
extract::{FromRequestParts, OptionalFromRequestParts}, extract::{FromRequestParts, OptionalFromRequestParts},
RequestPartsExt, RequestPartsExt,
}; };

View File

@ -1,7 +1,7 @@
use axum::extract::{FromRequest, Request};
use axum_core::__composite_rejection as composite_rejection; use axum_core::__composite_rejection as composite_rejection;
use axum_core::__define_rejection as define_rejection; use axum_core::__define_rejection as define_rejection;
use axum_core::extract::rejection::BytesRejection; use axum_core::extract::rejection::BytesRejection;
use axum_core::extract::{FromRequest, Request};
use bytes::Bytes; use bytes::Bytes;
use http::{header, HeaderMap}; use http::{header, HeaderMap};
use serde_core::Deserialize; use serde_core::Deserialize;

View File

@ -1,9 +1,15 @@
//! Additional extractors. //! Additional extractors.
mod cached;
mod host; mod host;
mod optional_path;
pub mod rejection; pub mod rejection;
#[cfg(feature = "optional-path")]
mod optional_path;
#[cfg(feature = "cached")]
mod cached;
#[cfg(feature = "with-rejection")]
mod with_rejection; mod with_rejection;
#[cfg(feature = "form")] #[cfg(feature = "form")]
@ -25,8 +31,16 @@ pub mod multipart;
mod scheme; mod scheme;
#[allow(deprecated)] #[allow(deprecated)]
#[cfg(feature = "optional-path")]
pub use self::optional_path::OptionalPath; pub use self::optional_path::OptionalPath;
pub use self::{cached::Cached, host::Host, with_rejection::WithRejection};
pub use self::host::Host;
#[cfg(feature = "cached")]
pub use self::cached::Cached;
#[cfg(feature = "with-rejection")]
pub use self::with_rejection::WithRejection;
#[cfg(feature = "cookie")] #[cfg(feature = "cookie")]
pub use self::cookie::CookieJar; pub use self::cookie::CookieJar;

View File

@ -2,14 +2,14 @@
//! //!
//! See [`Multipart`] for more details. //! See [`Multipart`] for more details.
use axum::{ use axum_core::{
body::{Body, Bytes}, RequestExt, __composite_rejection as composite_rejection,
__define_rejection as define_rejection,
body::Body,
extract::FromRequest, extract::FromRequest,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
RequestExt,
}; };
use axum_core::__composite_rejection as composite_rejection; use bytes::Bytes;
use axum_core::__define_rejection as define_rejection;
use futures_core::stream::Stream; use futures_core::stream::Stream;
use http::{ use http::{
header::{HeaderMap, CONTENT_TYPE}, header::{HeaderMap, CONTENT_TYPE},
@ -284,7 +284,7 @@ fn status_code_from_multer_error(err: &multer::Error) -> StatusCode {
} }
if err if err
.downcast_ref::<axum::Error>() .downcast_ref::<axum_core::Error>()
.and_then(|err| err.source()) .and_then(|err| err.source())
.and_then(|err| err.downcast_ref::<http_body_util::LengthLimitError>()) .and_then(|err| err.downcast_ref::<http_body_util::LengthLimitError>())
.is_some() .is_some()

View File

@ -1,6 +1,6 @@
use axum::extract::FromRequestParts;
use axum_core::__composite_rejection as composite_rejection; use axum_core::__composite_rejection as composite_rejection;
use axum_core::__define_rejection as define_rejection; use axum_core::__define_rejection as define_rejection;
use axum_core::extract::FromRequestParts;
use http::{request::Parts, Uri}; use http::{request::Parts, Uri};
use serde_core::de::DeserializeOwned; use serde_core::de::DeserializeOwned;

View File

@ -1,8 +1,7 @@
//! Extractor that parses the scheme of a request. //! Extractor that parses the scheme of a request.
//! See [`Scheme`] for more details. //! See [`Scheme`] for more details.
use axum::extract::FromRequestParts; use axum_core::{__define_rejection as define_rejection, extract::FromRequestParts};
use axum_core::__define_rejection as define_rejection;
use http::{ use http::{
header::{HeaderMap, FORWARDED}, header::{HeaderMap, FORWARDED},
request::Parts, request::Parts,

View File

@ -1,6 +1,6 @@
//! Newline delimited JSON extractor and response. //! Newline delimited JSON extractor and response.
use axum::{ use axum_core::{
body::Body, body::Body,
extract::{FromRequest, Request}, extract::{FromRequest, Request},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
@ -74,7 +74,7 @@ pin_project! {
}, },
Extractor { Extractor {
#[pin] #[pin]
stream: BoxStream<'static, Result<S, axum::Error>>, stream: BoxStream<'static, Result<S, axum_core::Error>>,
}, },
} }
} }
@ -117,9 +117,9 @@ where
let deserialized_stream = let deserialized_stream =
lines_stream lines_stream
.map_err(axum::Error::new) .map_err(axum_core::Error::new)
.and_then(|value| async move { .and_then(|value| async move {
serde_json::from_str::<T>(&value).map_err(axum::Error::new) serde_json::from_str::<T>(&value).map_err(axum_core::Error::new)
}); });
Ok(Self { Ok(Self {
@ -132,7 +132,7 @@ where
} }
impl<T> Stream for JsonLines<T, AsExtractor> { impl<T> Stream for JsonLines<T, AsExtractor> {
type Item = Result<T, axum::Error>; type Item = Result<T, axum_core::Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match self.project().inner.project() { match self.project().inner.project() {

View File

@ -11,6 +11,7 @@
//! ---|---|--- //! ---|---|---
//! `async-read-body` | Enables the [`AsyncReadBody`](crate::body::AsyncReadBody) body | No //! `async-read-body` | Enables the [`AsyncReadBody`](crate::body::AsyncReadBody) body | No
//! `attachment` | Enables the [`Attachment`](crate::response::Attachment) response | No //! `attachment` | Enables the [`Attachment`](crate::response::Attachment) response | No
//! `cached` | Enables the [`Cached`](crate::extract::Cached) extractor | No
//! `cookie` | Enables the [`CookieJar`](crate::extract::CookieJar) extractor | No //! `cookie` | Enables the [`CookieJar`](crate::extract::CookieJar) extractor | No
//! `cookie-private` | Enables the [`PrivateCookieJar`](crate::extract::PrivateCookieJar) extractor | No //! `cookie-private` | Enables the [`PrivateCookieJar`](crate::extract::PrivateCookieJar) extractor | No
//! `cookie-signed` | Enables the [`SignedCookieJar`](crate::extract::SignedCookieJar) extractor | No //! `cookie-signed` | Enables the [`SignedCookieJar`](crate::extract::SignedCookieJar) extractor | No
@ -18,15 +19,20 @@
//! `erased-json` | Enables the [`ErasedJson`](crate::response::ErasedJson) response | No //! `erased-json` | Enables the [`ErasedJson`](crate::response::ErasedJson) response | No
//! `error-response` | Enables the [`InternalServerError`](crate::response::InternalServerError) response | No //! `error-response` | Enables the [`InternalServerError`](crate::response::InternalServerError) response | No
//! `form` | Enables the [`Form`](crate::extract::Form) extractor | No //! `form` | Enables the [`Form`](crate::extract::Form) extractor | No
//! `handler` | Enables the [handler] utilities | No
//! `json-deserializer` | Enables the [`JsonDeserializer`](crate::extract::JsonDeserializer) extractor | No //! `json-deserializer` | Enables the [`JsonDeserializer`](crate::extract::JsonDeserializer) extractor | No
//! `json-lines` | Enables the [`JsonLines`](crate::extract::JsonLines) extractor and response | No //! `json-lines` | Enables the [`JsonLines`](crate::extract::JsonLines) extractor and response | No
//! `middleware` | Enables the [middleware] utilities | No
//! `multipart` | Enables the [`Multipart`](crate::extract::Multipart) extractor | No //! `multipart` | Enables the [`Multipart`](crate::extract::Multipart) extractor | No
//! `optional-path` | Enables the [`OptionalPath`](crate::extract::OptionalPath) extractor | No
//! `protobuf` | Enables the [`Protobuf`](crate::protobuf::Protobuf) extractor and response | No //! `protobuf` | Enables the [`Protobuf`](crate::protobuf::Protobuf) extractor and response | No
//! `query` | Enables the [`Query`](crate::extract::Query) extractor | No //! `query` | Enables the [`Query`](crate::extract::Query) extractor | No
//! `routing` | Enables the [routing] utilities | No
//! `tracing` | Log rejections from built-in extractors | Yes //! `tracing` | Log rejections from built-in extractors | Yes
//! `typed-routing` | Enables the [`TypedPath`](crate::routing::TypedPath) routing utilities | No //! `typed-routing` | Enables the [`TypedPath`](crate::routing::TypedPath) routing utilities | No
//! `typed-header` | Enables the [`TypedHeader`] extractor and response | No //! `typed-header` | Enables the [`TypedHeader`] extractor and response | No
//! `file-stream` | Enables the [`FileStream`](crate::response::FileStream) response | No //! `file-stream` | Enables the [`FileStream`](crate::response::FileStream) response | No
//! `with-rejection` | Enables the [`WithRejection`](crate::extract::WithRejection) extractor | No
//! //!
//! [`axum`]: https://crates.io/crates/axum //! [`axum`]: https://crates.io/crates/axum
@ -40,11 +46,17 @@ extern crate self as axum_extra;
pub mod body; pub mod body;
pub mod either; pub mod either;
pub mod extract; pub mod extract;
pub mod handler;
pub mod middleware;
pub mod response; pub mod response;
#[cfg(feature = "routing")]
pub mod routing; pub mod routing;
#[cfg(feature = "middleware")]
pub mod middleware;
#[cfg(feature = "handler")]
pub mod handler;
#[cfg(feature = "json-lines")] #[cfg(feature = "json-lines")]
pub mod json_lines; pub mod json_lines;

View File

@ -1,12 +1,12 @@
//! Protocol Buffer extractor and response. //! Protocol Buffer extractor and response.
use axum::{ use axum_core::__composite_rejection as composite_rejection;
use axum_core::__define_rejection as define_rejection;
use axum_core::{
extract::{rejection::BytesRejection, FromRequest, Request}, extract::{rejection::BytesRejection, FromRequest, Request},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
RequestExt, RequestExt,
}; };
use axum_core::__composite_rejection as composite_rejection;
use axum_core::__define_rejection as define_rejection;
use bytes::BytesMut; use bytes::BytesMut;
use http::StatusCode; use http::StatusCode;
use http_body_util::BodyExt; use http_body_util::BodyExt;

View File

@ -1,4 +1,4 @@
use axum::response::IntoResponse; use axum_core::response::IntoResponse;
use http::{header, HeaderMap, HeaderValue}; use http::{header, HeaderMap, HeaderValue};
use tracing::error; use tracing::error;
@ -79,7 +79,7 @@ impl<T> IntoResponse for Attachment<T>
where where
T: IntoResponse, T: IntoResponse,
{ {
fn into_response(self) -> axum::response::Response { fn into_response(self) -> axum_core::response::Response {
let mut headers = HeaderMap::new(); let mut headers = HeaderMap::new();
if let Some(content_type) = self.content_type { if let Some(content_type) = self.content_type {

View File

@ -1,10 +1,8 @@
use std::sync::Arc; use std::sync::Arc;
use axum::{ use axum_core::response::{IntoResponse, Response};
http::{header, HeaderValue, StatusCode},
response::{IntoResponse, Response},
};
use bytes::{BufMut, Bytes, BytesMut}; use bytes::{BufMut, Bytes, BytesMut};
use http::{header, HeaderValue, StatusCode};
use serde_core::Serialize; use serde_core::Serialize;
/// A response type that holds a JSON in serialized form. /// A response type that holds a JSON in serialized form.

View File

@ -1,4 +1,4 @@
use axum::{ use axum_core::{
body, body,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
BoxError, BoxError,

View File

@ -60,11 +60,11 @@ macro_rules! mime_response {
#[must_use] #[must_use]
pub struct $ident<T>(pub T); pub struct $ident<T>(pub T);
impl<T> axum::response::IntoResponse for $ident<T> impl<T> axum_core::response::IntoResponse for $ident<T>
where where
T: axum::response::IntoResponse, T: axum_core::response::IntoResponse,
{ {
fn into_response(self) -> axum::response::Response { fn into_response(self) -> axum_core::response::Response {
( (
[( [(
http::header::CONTENT_TYPE, http::header::CONTENT_TYPE,

View File

@ -1,6 +1,6 @@
//! Generate forms to use in responses. //! Generate forms to use in responses.
use axum::response::{IntoResponse, Response}; use axum_core::response::{IntoResponse, Response};
use fastrand; use fastrand;
use http::{header, HeaderMap, StatusCode}; use http::{header, HeaderMap, StatusCode};
use mime::Mime; use mime::Mime;

View File

@ -1,6 +1,6 @@
//! Extractor and response for typed headers. //! Extractor and response for typed headers.
use axum::{ use axum_core::{
extract::{FromRequestParts, OptionalFromRequestParts}, extract::{FromRequestParts, OptionalFromRequestParts},
response::{IntoResponse, IntoResponseParts, Response, ResponseParts}, response::{IntoResponse, IntoResponseParts, Response, ResponseParts},
}; };

View File

@ -36,7 +36,7 @@ syn = { version = "2.0", features = [
[dev-dependencies] [dev-dependencies]
axum = { path = "../axum", features = ["macros"] } axum = { path = "../axum", features = ["macros"] }
axum-extra = { path = "../axum-extra", features = ["typed-routing", "cookie-private", "typed-header"] } axum-extra = { path = "../axum-extra", features = ["typed-routing", "cookie-private", "typed-header", "routing"] }
rustversion = "1.0" rustversion = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"

View File

@ -6,7 +6,7 @@ publish = false
[dependencies] [dependencies]
axum = { path = "../../axum", features = ["macros"] } axum = { path = "../../axum", features = ["macros"] }
axum-extra = { path = "../../axum-extra" } axum-extra = { path = "../../axum-extra", features = ["with-rejection"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
thiserror = "1.0" thiserror = "1.0"