├── README.md ├── axum ├── LICENSE ├── src │ ├── routing │ │ ├── future.rs │ │ ├── not_found.rs │ │ ├── into_make_service.rs │ │ └── url_params.rs │ ├── docs │ │ ├── handlers_intro.md │ │ ├── method_routing │ │ │ ├── merge.md │ │ │ ├── layer.md │ │ │ ├── route_layer.md │ │ │ └── fallback.md │ │ ├── routing │ │ │ ├── without_v07_checks.md │ │ │ ├── method_not_allowed_fallback.md │ │ │ ├── route_layer.md │ │ │ └── fallback.md │ │ └── debugging_handler_type_errors.md │ ├── test_helpers │ │ ├── mod.rs │ │ └── counting_cloneable_state.rs │ ├── extract │ │ └── raw_query.rs │ ├── middleware │ │ └── mod.rs │ ├── handler │ │ └── future.rs │ └── body │ │ └── mod.rs └── tests │ └── panic_location.rs ├── axum-core ├── LICENSE ├── src │ ├── error.rs │ ├── extract │ │ └── from_ref.rs │ ├── lib.rs │ └── ext_traits │ │ └── mod.rs ├── README.md └── Cargo.toml ├── axum-extra ├── LICENSE ├── test_files │ ├── script.js │ ├── index.html │ └── index_2.html ├── src │ ├── body │ │ └── mod.rs │ ├── extract │ │ └── rejection.rs │ └── response │ │ └── error_response.rs └── README.md ├── axum-macros ├── LICENSE ├── tests │ ├── debug_handler │ │ ├── fail │ │ │ ├── .gitkeep │ │ │ ├── not_a_function.rs │ │ │ ├── not_async.rs │ │ │ ├── invalid_attrs.rs │ │ │ ├── generics.rs │ │ │ ├── argument_not_extractor.rs │ │ │ ├── not_a_function.stderr │ │ │ ├── duplicate_args.rs │ │ │ ├── not_async.stderr │ │ │ ├── wrong_return_type.rs │ │ │ ├── invalid_attrs.stderr │ │ │ ├── multiple_paths.rs │ │ │ ├── not_send.rs │ │ │ ├── json_not_deserialize.rs │ │ │ ├── duplicate_args.stderr │ │ │ ├── extract_self_ref.stderr │ │ │ ├── extract_self_mut.stderr │ │ │ ├── generics.stderr │ │ │ ├── single_wrong_return_tuple.rs │ │ │ ├── extension_not_clone.rs │ │ │ ├── wrong_order.rs │ │ │ ├── returning_request_parts.rs │ │ │ ├── multiple_request_consumers.rs │ │ │ ├── too_many_extractors.stderr │ │ │ ├── returning_request_parts.stderr │ │ │ ├── extract_self_ref.rs │ │ │ ├── extract_self_mut.rs │ │ │ ├── output_tuple_too_many.stderr │ │ │ ├── too_many_extractors.rs │ │ │ ├── wrong_order.stderr │ │ │ ├── multiple_request_consumers.stderr │ │ │ ├── multiple_paths.stderr │ │ │ ├── wrong_return_tuple.rs │ │ │ ├── wrong_return_type.stderr │ │ │ ├── not_send.stderr │ │ │ ├── single_wrong_return_tuple.stderr │ │ │ ├── output_tuple_too_many.rs │ │ │ ├── extension_not_clone.stderr │ │ │ └── argument_not_extractor.stderr │ │ └── pass │ │ │ ├── associated_fn_without_self.rs │ │ │ ├── ready.rs │ │ │ ├── deny_unreachable_code.rs │ │ │ ├── mut_extractor.rs │ │ │ ├── multiple_extractors.rs │ │ │ ├── request_last.rs │ │ │ ├── impl_future.rs │ │ │ ├── impl_into_response.rs │ │ │ ├── state_and_body.rs │ │ │ ├── returns_self.rs │ │ │ ├── set_state.rs │ │ │ ├── infer_state.rs │ │ │ └── self_receiver.rs │ ├── from_request │ │ ├── fail │ │ │ ├── enum_no_via.rs │ │ │ ├── not_enum_or_struct.rs │ │ │ ├── generic.rs │ │ │ ├── unknown_attr_container.rs │ │ │ ├── unknown_attr_field.rs │ │ │ ├── unknown_attr_container.stderr │ │ │ ├── unknown_attr_field.stderr │ │ │ ├── double_via_attr.rs │ │ │ ├── enum_from_request_on_variant.stderr │ │ │ ├── enum_from_request_on_variant.rs │ │ │ ├── generic.stderr │ │ │ ├── via_on_container_and_field.rs │ │ │ ├── enum_from_request_ident_in_variant.stderr │ │ │ ├── double_via_attr.stderr │ │ │ ├── generic_without_via.rs │ │ │ ├── enum_from_request_ident_in_variant.rs │ │ │ ├── parts_extracting_body.rs │ │ │ ├── enum_no_via.stderr │ │ │ ├── generic_without_via_rejection.rs │ │ │ ├── via_on_container_and_field.stderr │ │ │ ├── not_enum_or_struct.stderr │ │ │ ├── state_infer_multiple_different_types.stderr │ │ │ ├── state_infer_multiple_different_types.rs │ │ │ ├── override_rejection_on_enum_without_via.rs │ │ │ ├── parts_extracting_body.stderr │ │ │ ├── generic_without_via.stderr │ │ │ └── generic_without_via_rejection.stderr │ │ └── pass │ │ │ ├── tuple.rs │ │ │ ├── unit.rs │ │ │ ├── empty_named.rs │ │ │ ├── empty_tuple.rs │ │ │ ├── unit_parts.rs │ │ │ ├── tuple_parts.rs │ │ │ ├── empty_named_parts.rs │ │ │ ├── empty_tuple_parts.rs │ │ │ ├── enum_via.rs │ │ │ ├── enum_via_parts.rs │ │ │ ├── tuple_via.rs │ │ │ ├── tuple_via_parts.rs │ │ │ ├── tuple_same_type_twice.rs │ │ │ ├── state_infer.rs │ │ │ ├── state_infer_parts.rs │ │ │ ├── tuple_same_type_twice_parts.rs │ │ │ ├── container.rs │ │ │ ├── state_infer_multiple.rs │ │ │ ├── container_parts.rs │ │ │ ├── state_field_infer.rs │ │ │ ├── state_via_infer.rs │ │ │ ├── tuple_same_type_twice_via.rs │ │ │ ├── tuple_same_type_twice_via_parts.rs │ │ │ ├── state_cookie.rs │ │ │ ├── named_parts.rs │ │ │ ├── named.rs │ │ │ ├── state_via.rs │ │ │ ├── state_enum_via.rs │ │ │ ├── state_field_explicit.rs │ │ │ ├── state_explicit_parts.rs │ │ │ ├── named_via.rs │ │ │ ├── override_rejection_with_via_on_enum.rs │ │ │ ├── state_via_parts.rs │ │ │ ├── named_via_parts.rs │ │ │ ├── override_rejection_with_via_on_enum_parts.rs │ │ │ ├── state_with_rejection.rs │ │ │ ├── state_enum_via_parts.rs │ │ │ ├── override_rejection_with_via_on_struct.rs │ │ │ ├── override_rejection_non_generic.rs │ │ │ ├── override_rejection_with_via_on_struct_parts.rs │ │ │ ├── override_rejection_non_generic_parts.rs │ │ │ ├── state_explicit.rs │ │ │ ├── override_rejection.rs │ │ │ └── override_rejection_parts.rs │ ├── from_ref │ │ ├── fail │ │ │ ├── generics.rs │ │ │ └── generics.stderr │ │ └── pass │ │ │ ├── reference-types.rs │ │ │ ├── skip.rs │ │ │ └── basic.rs │ ├── typed_path │ │ ├── fail │ │ │ ├── route_not_starting_with_slash.rs │ │ │ ├── route_not_starting_with_slash_non_empty.rs │ │ │ ├── not_deserialize.rs │ │ │ ├── missing_field.rs │ │ │ ├── unit_with_capture.rs │ │ │ ├── missing_capture.rs │ │ │ ├── route_not_starting_with_slash.stderr │ │ │ ├── unit_with_capture.stderr │ │ │ ├── route_not_starting_with_slash_non_empty.stderr │ │ │ ├── missing_field.stderr │ │ │ └── missing_capture.stderr │ │ └── pass │ │ │ ├── wildcards.rs │ │ │ ├── unit_struct.rs │ │ │ ├── tuple_struct.rs │ │ │ ├── into_uri.rs │ │ │ ├── url_encoding.rs │ │ │ ├── named_fields_struct.rs │ │ │ ├── result_handler.rs │ │ │ └── customize_rejection.rs │ └── debug_middleware │ │ ├── pass │ │ └── basic.rs │ │ └── fail │ │ ├── next_not_last.stderr │ │ ├── next_not_last.rs │ │ ├── takes_next_twice.rs │ │ ├── doesnt_take_next.rs │ │ ├── doesnt_take_next.stderr │ │ └── takes_next_twice.stderr ├── rust-toolchain ├── src │ └── axum_test.rs ├── Cargo.toml └── README.md ├── .gitignore ├── examples ├── sse │ ├── assets │ │ ├── index.html │ │ └── script.js │ └── Cargo.toml ├── static-file-server │ ├── assets │ │ ├── index.html │ │ └── script.js │ └── Cargo.toml ├── templates │ ├── templates │ │ └── hello.html │ └── Cargo.toml ├── async-graphql │ └── README.md ├── compression │ ├── data │ │ ├── products.json.gz │ │ └── products.json │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── diesel-postgres │ ├── migrations │ │ └── 2023-03-14-180127_add_users │ │ │ ├── down.sql │ │ │ └── up.sql │ └── Cargo.toml ├── diesel-async-postgres │ ├── migrations │ │ └── 2023-03-14-180127_add_users │ │ │ ├── down.sql │ │ │ └── up.sql │ └── Cargo.toml ├── websockets │ ├── assets │ │ ├── index.html │ │ └── script.js │ └── Cargo.toml ├── templates-minijinja │ ├── templates │ │ ├── about.jinja │ │ ├── home.jinja │ │ ├── content.jinja │ │ └── layout.jinja │ └── Cargo.toml ├── hello-world │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── auto-reload │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── README.md ├── cors │ └── Cargo.toml ├── routes-and-handlers-close-together │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── websockets-http2 │ ├── assets │ │ ├── index.html │ │ └── script.js │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── global-404-handler │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── tokio-redis │ └── Cargo.toml ├── reverse-proxy │ └── Cargo.toml ├── readme │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── print-request-response │ └── Cargo.toml ├── anyhow-error-response │ └── Cargo.toml ├── consume-body-in-extractor-or-middleware │ └── Cargo.toml ├── customize-path-rejection │ └── Cargo.toml ├── tokio-postgres │ └── Cargo.toml ├── graceful-shutdown │ └── Cargo.toml ├── query-params-with-empty-strings │ └── Cargo.toml ├── handle-head-request │ └── Cargo.toml ├── request-id │ └── Cargo.toml ├── testing-websockets │ └── Cargo.toml ├── chat │ └── Cargo.toml ├── parse-body-based-on-content-type │ └── Cargo.toml ├── sqlx-postgres │ └── Cargo.toml ├── multipart-form │ └── Cargo.toml ├── serve-with-hyper │ └── Cargo.toml ├── tracing-aka-logging │ └── Cargo.toml ├── tls-rustls │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── versioning │ └── Cargo.toml ├── tls-graceful-shutdown │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── http-proxy │ └── Cargo.toml ├── form │ └── Cargo.toml ├── error-handling │ └── Cargo.toml ├── mongodb │ └── Cargo.toml ├── prometheus-metrics │ └── Cargo.toml ├── reqwest-response │ └── Cargo.toml ├── unix-domain-socket │ └── Cargo.toml ├── dependency-injection │ └── Cargo.toml ├── low-level-native-tls │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── low-level-rustls │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── stream-to-file │ └── Cargo.toml ├── jwt │ └── Cargo.toml ├── low-level-openssl │ ├── Cargo.toml │ └── self_signed_certs │ │ ├── cert.pem │ │ └── key.pem ├── customize-extractor-error │ ├── Cargo.toml │ ├── README.md │ └── src │ │ └── main.rs ├── todos │ └── Cargo.toml ├── validator │ └── Cargo.toml ├── key-value-store │ └── Cargo.toml ├── testing │ └── Cargo.toml ├── oauth │ └── Cargo.toml └── simple-router-wasm │ └── Cargo.toml ├── CHANGELOG.md ├── .clippy.toml ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature_request.md │ └── bug_report.md ├── DISCUSSION_TEMPLATE │ └── q-a.yml └── PULL_REQUEST_TEMPLATE.md ├── deny.toml └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | axum/README.md -------------------------------------------------------------------------------- /axum/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /axum-core/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /axum-extra/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /axum-macros/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .DS_Store 3 | .vscode 4 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /axum-extra/test_files/script.js: -------------------------------------------------------------------------------- 1 | console.log('hi') 2 | -------------------------------------------------------------------------------- /axum-macros/rust-toolchain: -------------------------------------------------------------------------------- 1 | nightly-2025-09-28 2 | -------------------------------------------------------------------------------- /axum-extra/test_files/index.html: -------------------------------------------------------------------------------- 1 |

Hello, World!

2 | -------------------------------------------------------------------------------- /examples/sse/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/static-file-server/assets/index.html: -------------------------------------------------------------------------------- 1 | Hi from index.html 2 | -------------------------------------------------------------------------------- /axum-extra/test_files/index_2.html: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /examples/templates/templates/hello.html: -------------------------------------------------------------------------------- 1 |

Hello, {{ name }}!

2 | -------------------------------------------------------------------------------- /examples/static-file-server/assets/script.js: -------------------------------------------------------------------------------- 1 | console.log("Hello, World!"); 2 | -------------------------------------------------------------------------------- /examples/async-graphql/README.md: -------------------------------------------------------------------------------- 1 | See . 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | axum's changelog has moved and now lives [here](https://github.com/tokio-rs/axum/blob/main/axum/CHANGELOG.md). 2 | -------------------------------------------------------------------------------- /examples/compression/data/products.json.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sferik/axum/main/examples/compression/data/products.json.gz -------------------------------------------------------------------------------- /examples/diesel-postgres/migrations/2023-03-14-180127_add_users/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in "up.sql" 2 | DROP TABLE "users"; 3 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_a_function.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | struct A; 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_async.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/migrations/2023-03-14-180127_add_users/down.sql: -------------------------------------------------------------------------------- 1 | -- This file should undo anything in "up.sql" 2 | DROP TABLE "users"; 3 | -------------------------------------------------------------------------------- /examples/websockets/assets/index.html: -------------------------------------------------------------------------------- 1 | Open the console to see stuff, then refresh to initiate exchange. 2 | 3 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/invalid_attrs.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler(foo)] 4 | async fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_no_via.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | enum Extractor {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/not_enum_or_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | union Extractor {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/generics.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(_extract: T) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(Option); 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/argument_not_extractor.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(_foo: bool) {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_a_function.stderr: -------------------------------------------------------------------------------- 1 | error: expected `fn` 2 | --> tests/debug_handler/fail/not_a_function.rs:4:1 3 | | 4 | 4 | struct A; 5 | | ^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/fail/generics.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::FromRef; 2 | 3 | #[derive(Clone, FromRef)] 4 | struct AppState { 5 | foo: T, 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/duplicate_args.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler(state = (), state = ())] 4 | async fn handler() {} 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_async.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must be `async fn`s 2 | --> tests/debug_handler/fail/not_async.rs:4:1 3 | | 4 | 4 | fn handler() {} 5 | | ^^ 6 | -------------------------------------------------------------------------------- /axum/src/routing/future.rs: -------------------------------------------------------------------------------- 1 | //! Future types. 2 | 3 | pub use super::{ 4 | into_make_service::IntoMakeServiceFuture, 5 | route::{InfallibleRouteFuture, RouteFuture}, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/sse/assets/script.js: -------------------------------------------------------------------------------- 1 | var eventSource = new EventSource('sse'); 2 | 3 | eventSource.onmessage = function(event) { 4 | console.log('Message from server ', event.data); 5 | } 6 | -------------------------------------------------------------------------------- /.clippy.toml: -------------------------------------------------------------------------------- 1 | allow-mixed-uninlined-format-args = false 2 | disallowed-types = [ 3 | { path = "tower::util::BoxCloneService", reason = "Use tower::util::BoxCloneSyncService instead" }, 4 | ] 5 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_type.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler() -> bool { 5 | false 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_container.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | #[from_request(foo)] 5 | struct Extractor; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(#[from_request(foo)] String); 5 | 6 | fn main() {} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("")] 5 | struct MyPath; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/invalid_attrs.stderr: -------------------------------------------------------------------------------- 1 | error: expected `state` 2 | --> tests/debug_handler/fail/invalid_attrs.rs:3:17 3 | | 4 | 3 | #[debug_handler(foo)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-extra/src/body/mod.rs: -------------------------------------------------------------------------------- 1 | //! Additional bodies. 2 | 3 | #[cfg(feature = "async-read-body")] 4 | mod async_read_body; 5 | 6 | #[cfg(feature = "async-read-body")] 7 | pub use self::async_read_body::AsyncReadBody; 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/fail/generics.stderr: -------------------------------------------------------------------------------- 1 | error: `#[derive(FromRef)]` doesn't support generics 2 | --> tests/from_ref/fail/generics.rs:4:16 3 | | 4 | 4 | struct AppState { 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("{foo}")] 5 | struct MyPath; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /examples/diesel-postgres/migrations/2023-03-14-180127_add_users/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE "users"( 3 | "id" SERIAL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "hair_color" TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/not_deserialize.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("/users/{id}")] 5 | struct MyPath { 6 | id: u32, 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_paths.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Path; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: Path, _: Path) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/migrations/2023-03-14-180127_add_users/up.sql: -------------------------------------------------------------------------------- 1 | -- Your SQL goes here 2 | CREATE TABLE "users"( 3 | "id" SERIAL PRIMARY KEY, 4 | "name" TEXT NOT NULL, 5 | "hair_color" TEXT 6 | ); 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/associated_fn_without_self.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | struct A; 4 | 5 | impl A { 6 | #[debug_handler] 7 | async fn handler() {} 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/ready.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | use std::future::{ready, Ready}; 3 | 4 | #[debug_handler] 5 | fn handler() -> Ready<()> { 6 | ready(()) 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/reference-types.rs: -------------------------------------------------------------------------------- 1 | #![deny(noop_method_call)] 2 | 3 | use axum_macros::FromRef; 4 | 5 | #[derive(FromRef)] 6 | struct State { 7 | inner: &'static str, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{id}")] 6 | struct MyPath {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/unit_with_capture.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{id}")] 6 | struct MyPath; 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_send.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler() { 5 | let _rc = std::rc::Rc::new(()); 6 | async {}.await; 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/deny_unreachable_code.rs: -------------------------------------------------------------------------------- 1 | #![deny(unreachable_code)] 2 | 3 | use axum::extract::Path; 4 | 5 | #[axum_macros::debug_handler] 6 | async fn handler(Path(_): Path) {} 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/mut_extractor.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | 3 | #[debug_handler] 4 | async fn handler(mut foo: String) -> String { 5 | foo += "bar"; 6 | foo 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/about.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 |

{{ about_text }}

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/home.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 |

{{ welcome_text }}

6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/multiple_extractors.rs: -------------------------------------------------------------------------------- 1 | use axum::http::{Method, Uri}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_one: Method, _two: Uri, _three: String) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/request_last.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{Extension, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: Extension, _: Request) {} 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/json_not_deserialize.rs: -------------------------------------------------------------------------------- 1 | use axum::Json; 2 | use axum_macros::debug_handler; 3 | 4 | struct Struct {} 5 | 6 | #[debug_handler] 7 | async fn handler(_foo: Json) {} 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/impl_future.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::debug_handler; 2 | use std::future::Future; 3 | 4 | #[debug_handler] 5 | fn handler() -> impl Future { 6 | async {} 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/skip.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRef; 2 | 3 | #[derive(Clone, FromRef)] 4 | struct AppState { 5 | auth_token: String, 6 | #[from_ref(skip)] 7 | also_string: String, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_container.stderr: -------------------------------------------------------------------------------- 1 | error: expected one of: `via`, `rejection`, `state` 2 | --> tests/from_request/fail/unknown_attr_container.rs:4:16 3 | | 4 | 4 | #[from_request(foo)] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/duplicate_args.stderr: -------------------------------------------------------------------------------- 1 | error: `state` specified more than once 2 | --> tests/debug_handler/fail/duplicate_args.rs:3:29 3 | | 4 | 3 | #[debug_handler(state = (), state = ())] 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_ref.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must only take owned values 2 | --> tests/debug_handler/fail/extract_self_ref.rs:19:22 3 | | 4 | 19 | async fn handler(&self) {} 5 | | ^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/impl_into_response.rs: -------------------------------------------------------------------------------- 1 | use axum::response::IntoResponse; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler() -> impl IntoResponse { 6 | "hi!" 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/unknown_attr_field.stderr: -------------------------------------------------------------------------------- 1 | error: expected `via` 2 | --> tests/from_request/fail/unknown_attr_field.rs:4:33 3 | | 4 | 4 | struct Extractor(#[from_request(foo)] String); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_mut.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers must only take owned values 2 | --> tests/debug_handler/fail/extract_self_mut.rs:19:22 3 | | 4 | 19 | async fn handler(&mut self) {} 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/generics.stderr: -------------------------------------------------------------------------------- 1 | error: `#[axum_macros::debug_handler]` doesn't support generic functions 2 | --> tests/debug_handler/fail/generics.rs:4:17 3 | | 4 | 4 | async fn handler(_extract: T) {} 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | struct NotIntoResponse; 4 | 5 | #[axum::debug_handler] 6 | async fn handler() -> (NotIntoResponse) { 7 | panic!() 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_capture.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users")] 6 | struct MyPath { 7 | id: u32, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash.stderr: -------------------------------------------------------------------------------- 1 | error: paths must start with a `/`. Use "/" for root routes 2 | --> tests/typed_path/fail/route_not_starting_with_slash.rs:4:14 3 | | 4 | 4 | #[typed_path("")] 5 | | ^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/unit_with_capture.stderr: -------------------------------------------------------------------------------- 1 | error: Typed paths for unit structs cannot contain captures 2 | --> tests/typed_path/fail/unit_with_capture.rs:5:14 3 | | 4 | 5 | #[typed_path("/users/{id}")] 5 | | ^^^^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /examples/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-hello-world" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/route_not_starting_with_slash_non_empty.stderr: -------------------------------------------------------------------------------- 1 | error: paths must start with a `/` 2 | --> tests/typed_path/fail/route_not_starting_with_slash_non_empty.rs:4:14 3 | | 4 | 4 | #[typed_path("{foo}")] 5 | | ^^^^^^^ 6 | -------------------------------------------------------------------------------- /examples/compression/data/products.json: -------------------------------------------------------------------------------- 1 | { 2 | "products": [ 3 | { 4 | "id": 1, 5 | "name": "Product 1" 6 | }, 7 | { 8 | "id": 2, 9 | "name": "Product 2" 10 | } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /examples/auto-reload/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "auto-reload" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | listenfd = "1.0.1" 10 | tokio = { version = "1.0", features = ["full"] } 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extension_not_clone.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Extension; 2 | use axum_macros::debug_handler; 3 | 4 | struct NonCloneType; 5 | 6 | #[debug_handler] 7 | async fn test_extension_non_clone(_: Extension) {} 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_order.rs: -------------------------------------------------------------------------------- 1 | use axum::{http::Uri, Json}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn one(_: Json<()>, _: Uri) {} 6 | 7 | #[debug_handler] 8 | async fn two(_: String, _: Uri) {} 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/double_via_attr.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State); 5 | 6 | #[derive(Clone)] 7 | struct State; 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | 3 | This folder contains numerous examples showing how to use axum. Each example is 4 | setup as its own crate so its dependencies are clear. 5 | 6 | For a list of what the community built with axum, please see the list 7 | [here](../ECOSYSTEM.md). 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/pass/basic.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(request: Request, next: Next) -> Response { 5 | next.run(request).await 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_on_variant.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` cannot be used on variants 2 | --> tests/from_request/fail/enum_from_request_on_variant.rs:6:20 3 | | 4 | 6 | #[from_request(via(axum::Extension))] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_field.stderr: -------------------------------------------------------------------------------- 1 | error[E0026]: struct `MyPath` does not have a field named `id` 2 | --> tests/typed_path/fail/missing_field.rs:5:14 3 | | 4 | 5 | #[typed_path("/users/{id}")] 5 | | ^^^^^^^^^^^^^ struct `MyPath` does not have this field 6 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/returning_request_parts.rs: -------------------------------------------------------------------------------- 1 | #[axum::debug_handler] 2 | async fn handler() -> ( 3 | axum::http::request::Parts, // this should be response parts, not request parts 4 | axum::http::StatusCode, 5 | ) { 6 | panic!() 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/next_not_last.stderr: -------------------------------------------------------------------------------- 1 | error: `axum::middleware::Next` must the last argument 2 | --> tests/debug_middleware/fail/next_not_last.rs:4:24 3 | | 4 | 4 | async fn my_middleware(next: Next, request: Request) -> Response { 5 | | ^^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_on_variant.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | #[from_request(via(axum::Extension))] 5 | enum Extractor { 6 | #[from_request(via(axum::Extension))] 7 | Foo, 8 | } 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/next_not_last.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(next: Next, request: Request) -> Response { 5 | next.run(request).await 6 | } 7 | 8 | fn main() {} 9 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics on tuple structs that have exactly one field of the generic type 2 | --> tests/from_request/fail/generic.rs:4:21 3 | | 4 | 4 | struct Extractor(Option); 5 | | ^^^^^^^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(axum::http::HeaderMap, String); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<()>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /examples/templates-minijinja/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-templates-minijinja" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | minijinja = "2.3.1" 10 | tokio = { version = "1.0", features = ["full"] } 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/unit.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor; 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /examples/cors/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-cors" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["cors"] } 11 | -------------------------------------------------------------------------------- /examples/routes-and-handlers-close-together/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-routes-and-handlers-close-together" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/via_on_container_and_field.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | #[from_request(via(axum::Extension))] 5 | struct Extractor(#[from_request(via(axum::Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /examples/websockets-http2/assets/index.html: -------------------------------------------------------------------------------- 1 |

Open this page in two windows and try sending some messages!

2 |
3 | 4 | 5 |
6 |
7 | 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/state_and_body.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::Request, extract::State}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler(state = AppState)] 5 | async fn handler(_: State, _: Request) {} 6 | 7 | #[derive(Clone)] 8 | struct AppState; 9 | 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_ident_in_variant.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` cannot be used inside variants 2 | --> tests/from_request/fail/enum_from_request_ident_in_variant.rs:7:24 3 | | 4 | 7 | #[from_request(via(axum::Extension))] 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_named.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor {} 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_tuple.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest)] 4 | struct Extractor(); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequest<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/double_via_attr.stderr: -------------------------------------------------------------------------------- 1 | error: `via` specified more than once 2 | --> tests/from_request/fail/double_via_attr.rs:4:55 3 | | 4 | 4 | struct Extractor(#[from_request(via(axum::Extension), via(axum::Extension))] State); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/content.jinja: -------------------------------------------------------------------------------- 1 | {% extends "layout" %} 2 | {% block title %}{{ super() }} | {{ title }} {% endblock %} 3 | {% block body %} 4 |

{{ title }}

5 | {% for data_entry in entries %} 6 |
    7 |
  • {{ data_entry }}
  • 8 |
9 | {% endfor %} 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | struct Extractor(T); 6 | 7 | async fn foo(_: Extractor<()>) {} 8 | 9 | fn main() { 10 | _ = Router::<()>::new().route("/", get(foo)); 11 | } 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/unit_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor; 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor(axum::http::HeaderMap, axum::http::Method); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<()>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_named_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor {} 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/empty_tuple_parts.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequestParts; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor(); 5 | 6 | fn assert_from_request() 7 | where 8 | Extractor: axum::extract::FromRequestParts<(), Rejection = std::convert::Infallible>, 9 | { 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: 🙏 Q&A (GitHub Discussions) 3 | url: https://github.com/tokio-rs/axum/discussions/categories/q-a 4 | about: Q&A all around axum usage 5 | - name: 💬 Tokio Discord 6 | url: https://discord.gg/tokio 7 | about: Community chat for Tokio (axum channel is under libs) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_from_request_ident_in_variant.rs: -------------------------------------------------------------------------------- 1 | use axum_macros::FromRequest; 2 | 3 | #[derive(FromRequest, Clone)] 4 | #[from_request(via(axum::Extension))] 5 | enum Extractor { 6 | Foo { 7 | #[from_request(via(axum::Extension))] 8 | foo: (), 9 | }, 10 | } 11 | 12 | fn main() {} 13 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/takes_next_twice.rs: -------------------------------------------------------------------------------- 1 | use axum::{debug_middleware, extract::Request, middleware::Next, response::Response}; 2 | 3 | #[debug_middleware] 4 | async fn my_middleware(request: Request, next: Next, next2: Next) -> Response { 5 | let _ = next2; 6 | next.run(request).await 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/doesnt_take_next.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | debug_middleware, 3 | extract::Request, 4 | response::{IntoResponse, Response}, 5 | }; 6 | 7 | #[debug_middleware] 8 | async fn my_middleware(request: Request) -> Response { 9 | let _ = request; 10 | ().into_response() 11 | } 12 | 13 | fn main() {} 14 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/enum_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Extension, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | #[from_request(via(Extension))] 6 | enum Extractor {} 7 | 8 | async fn foo(_: Extractor) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/wildcards.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::{RouterExt, TypedPath}; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/{*rest}")] 6 | struct MyPath { 7 | rest: String, 8 | } 9 | 10 | fn main() { 11 | _ = axum::Router::<()>::new().typed_get(|_: MyPath| async {}); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/parts_extracting_body.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequestParts, response::Response}; 2 | 3 | #[derive(FromRequestParts)] 4 | struct Extractor { 5 | body: String, 6 | } 7 | 8 | fn assert_from_request() 9 | where 10 | Extractor: FromRequestParts<(), Rejection = Response>, 11 | { 12 | } 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/enum_no_via.stderr: -------------------------------------------------------------------------------- 1 | error: missing `#[from_request(via(...))]` 2 | --> tests/from_request/fail/enum_no_via.rs:3:10 3 | | 4 | 3 | #[derive(FromRequest, Clone)] 5 | | ^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FromRequest` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /examples/global-404-handler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-global-404-handler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/enum_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Extension, Router}; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts, Clone)] 5 | #[from_request(via(Extension))] 6 | enum Extractor {} 7 | 8 | async fn foo(_: Extractor) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest, Clone)] 5 | #[from_request(rejection(Foo))] 6 | struct Extractor(T); 7 | 8 | async fn foo(_: Extractor<()>) {} 9 | 10 | fn main() { 11 | _ = Router::<()>::new().route("/", get(foo)); 12 | } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/via_on_container_and_field.stderr: -------------------------------------------------------------------------------- 1 | error: `#[from_request(via(...))]` on a field cannot be used together with `#[from_request(...)]` on the container 2 | --> tests/from_request/fail/via_on_container_and_field.rs:5:33 3 | | 4 | 5 | struct Extractor(#[from_request(via(axum::Extension))] State); 5 | | ^^^ 6 | -------------------------------------------------------------------------------- /examples/tokio-redis/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tokio-redis" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | bb8-redis = "0.24" 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_via.rs: -------------------------------------------------------------------------------- 1 | use axum::Extension; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor(#[from_request(via(Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn assert_from_request() 11 | where 12 | Extractor: axum::extract::FromRequest<()>, 13 | { 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /examples/reverse-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-reverse-proxy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0.0", features = ["full"] } 10 | hyper-util = { version = "0.1.1", features = ["client-legacy"] } 11 | tokio = { version = "1", features = ["full"] } 12 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_request_consumers.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | body::Bytes, 3 | http::{Method, Uri}, 4 | Json, 5 | }; 6 | use axum_macros::debug_handler; 7 | 8 | #[debug_handler] 9 | async fn one(_: Json<()>, _: String, _: Uri) {} 10 | 11 | #[debug_handler] 12 | async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {} 13 | 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/not_enum_or_struct.stderr: -------------------------------------------------------------------------------- 1 | error: expected `struct` or `enum` 2 | --> tests/from_request/fail/not_enum_or_struct.rs:4:1 3 | | 4 | 4 | union Extractor {} 5 | | ^^^^^^^^^^^^^^^^^^ 6 | 7 | error: unions cannot have zero fields 8 | --> tests/from_request/fail/not_enum_or_struct.rs:4:1 9 | | 10 | 4 | union Extractor {} 11 | | ^^^^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/unit_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | 3 | #[derive(TypedPath)] 4 | #[typed_path("/users")] 5 | struct MyPath; 6 | 7 | fn main() { 8 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 9 | 10 | assert_eq!(MyPath::PATH, "/users"); 11 | assert_eq!(format!("{}", MyPath), "/users"); 12 | } 13 | -------------------------------------------------------------------------------- /examples/readme/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-readme" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::Extension; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts)] 5 | struct Extractor(#[from_request(via(Extension))] State); 6 | 7 | #[derive(Clone)] 8 | struct State; 9 | 10 | fn assert_from_request() 11 | where 12 | Extractor: axum::extract::FromRequestParts<()>, 13 | { 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /examples/print-request-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-print-request-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/anyhow-error-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-anyhow-error-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1.0" 9 | axum = { path = "../../axum" } 10 | tokio = { version = "1.0", features = ["full"] } 11 | 12 | [dev-dependencies] 13 | http-body-util = "0.1.0" 14 | tower = { version = "0.5.2", features = ["util"] } 15 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/returns_self.rs: -------------------------------------------------------------------------------- 1 | use axum::response::{IntoResponse, Response}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl A { 7 | #[debug_handler] 8 | async fn handler() -> Self { 9 | A 10 | } 11 | } 12 | 13 | impl IntoResponse for A { 14 | fn into_response(self) -> Response { 15 | todo!() 16 | } 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/doesnt_take_next.stderr: -------------------------------------------------------------------------------- 1 | error: Middleware functions must take `axum::middleware::Next` as the last argument 2 | --> tests/debug_middleware/fail/doesnt_take_next.rs:7:1 3 | | 4 | 7 | #[debug_middleware] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_middleware/fail/takes_next_twice.stderr: -------------------------------------------------------------------------------- 1 | error: Middleware functions can only take one argument of type `axum::middleware::Next` 2 | --> tests/debug_middleware/fail/takes_next_twice.rs:3:1 3 | | 4 | 3 | #[debug_middleware] 5 | | ^^^^^^^^^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the attribute macro `debug_middleware` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /examples/consume-body-in-extractor-or-middleware/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-consume-body-in-extractor-or-middleware" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/customize-path-rejection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-customize-path-rejection" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/tokio-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tokio-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | bb8 = "0.9.0" 10 | bb8-postgres = "0.9.0" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tokio-postgres = "0.7.2" 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/graceful-shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-graceful-shutdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["timeout", "trace"] } 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | -------------------------------------------------------------------------------- /examples/query-params-with-empty-strings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-query-params-with-empty-strings" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | serde = { version = "1.0", features = ["derive"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["util"] } 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum_macros::FromRequest; 3 | use serde::Deserialize; 4 | 5 | #[derive(FromRequest)] 6 | struct Extractor(Query, axum::extract::Json); 7 | 8 | #[derive(Deserialize)] 9 | struct Payload {} 10 | 11 | fn assert_from_request() 12 | where 13 | Extractor: axum::extract::FromRequest<()>, 14 | { 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /examples/handle-head-request/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-handle-head-request" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | 11 | [dev-dependencies] 12 | http-body-util = "0.1.0" 13 | hyper = { version = "1.0.0", features = ["full"] } 14 | tower = { version = "0.5.2", features = ["util"] } 15 | -------------------------------------------------------------------------------- /examples/request-id/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-request-id" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = "0.5.2" 11 | tower-http = { version = "0.6", features = ["request-id", "trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/testing-websockets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-testing-websockets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws"] } 9 | futures-channel = "0.3" 10 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tokio-tungstenite = "0.28" 13 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/too_many_extractors.stderr: -------------------------------------------------------------------------------- 1 | error: Handlers cannot take more than 16 arguments. Use `(a, b): (ExtractorA, ExtractorA)` to further nest extractors 2 | --> tests/debug_handler/fail/too_many_extractors.rs:6:5 3 | | 4 | 6 | / _e1: Uri, 5 | 7 | | _e2: Uri, 6 | 8 | | _e3: Uri, 7 | 9 | | _e4: Uri, 8 | ... | 9 | 21 | | _e16: Uri, 10 | 22 | | _e17: Uri, 11 | | |______________^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | } 8 | 9 | #[derive(Clone)] 10 | struct AppState {} 11 | 12 | fn assert_from_request() 13 | where 14 | Extractor: axum::extract::FromRequest, 15 | { 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /axum/src/docs/handlers_intro.md: -------------------------------------------------------------------------------- 1 | In axum a "handler" is an async function that accepts zero or more 2 | ["extractors"](crate::extract) as arguments and returns something that 3 | can be converted [into a response](crate::response). 4 | 5 | Handlers are where your application logic lives and axum applications are built 6 | by routing between handlers. 7 | 8 | [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html 9 | -------------------------------------------------------------------------------- /examples/chat/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-chat" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws"] } 9 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 10 | tokio = { version = "1", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/parse-body-based-on-content-type/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-parse-body-based-on-content-type" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/sqlx-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-sqlx-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | 13 | sqlx = { version = "0.8", features = ["runtime-tokio-rustls", "any", "postgres"] } 14 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/state_infer_multiple_different_types.stderr: -------------------------------------------------------------------------------- 1 | error: can't infer state type, please add `#[from_request(state = MyStateType)]` attribute 2 | --> tests/from_request/fail/state_infer_multiple_different_types.rs:4:10 3 | | 4 | 4 | #[derive(FromRequest)] 5 | | ^^^^^^^^^^^ 6 | | 7 | = note: this error originates in the derive macro `FromRequest` (in Nightly builds, run with -Z macro-backtrace for more info) 8 | -------------------------------------------------------------------------------- /examples/multipart-form/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-multipart-form" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["multipart"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["limit", "trace"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/serve-with-hyper/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-serve-with-hyper" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0", features = [] } 10 | hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["util"] } 13 | -------------------------------------------------------------------------------- /examples/tracing-aka-logging/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tracing-aka-logging" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing"] } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower-http = { version = "0.6.1", features = ["trace"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | -------------------------------------------------------------------------------- /examples/tls-rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tls-rustls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra" } 10 | axum-server = { version = "0.7", features = ["tls-rustls"] } 11 | tokio = { version = "1", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/templates-minijinja/templates/layout.jinja: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% block title %}Website Name{% endblock %} 4 | 5 | 12 | {% block body %}{% endblock %} 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/versioning/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-versioning" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tracing = "0.1" 11 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 12 | 13 | [dev-dependencies] 14 | http-body-util = "0.1.0" 15 | tower = { version = "0.5.2", features = ["util"] } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequestParts; 3 | 4 | #[derive(FromRequestParts)] 5 | struct Extractor { 6 | inner_state: State, 7 | } 8 | 9 | #[derive(Clone)] 10 | struct AppState {} 11 | 12 | fn assert_from_request() 13 | where 14 | Extractor: axum::extract::FromRequestParts, 15 | { 16 | } 17 | 18 | fn main() {} 19 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum_macros::FromRequestParts; 3 | use serde::Deserialize; 4 | 5 | #[derive(FromRequestParts)] 6 | struct Extractor(Query, axum::extract::Path); 7 | 8 | #[derive(Deserialize)] 9 | struct Payload {} 10 | 11 | fn assert_from_request() 12 | where 13 | Extractor: axum::extract::FromRequestParts<()>, 14 | { 15 | } 16 | 17 | fn main() {} 18 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/returning_request_parts.stderr: -------------------------------------------------------------------------------- 1 | error[E0308]: mismatched types 2 | --> tests/debug_handler/fail/returning_request_parts.rs:3:5 3 | | 4 | 3 | axum::http::request::Parts, // this should be response parts, not request parts 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | | | 7 | | expected `axum::http::response::Parts`, found `axum::http::request::Parts` 8 | | expected `axum::http::response::Parts` because of return type 9 | -------------------------------------------------------------------------------- /examples/websockets-http2/assets/script.js: -------------------------------------------------------------------------------- 1 | const socket = new WebSocket('wss://localhost:3000/ws'); 2 | 3 | socket.addEventListener('message', e => { 4 | document.getElementById("messages").append(e.data, document.createElement("br")); 5 | }); 6 | 7 | const form = document.querySelector("form"); 8 | form.addEventListener("submit", () => { 9 | socket.send(form.elements.namedItem("content").value); 10 | form.elements.namedItem("content").value = ""; 11 | }); 12 | -------------------------------------------------------------------------------- /examples/templates/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-templates" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | askama = "0.14" 9 | axum = { path = "../../axum" } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | 14 | [dev-dependencies] 15 | http-body-util = "0.1.0" 16 | tower = { version = "0.5.2", features = ["util"] } 17 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-tls-graceful-shutdown" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra" } 10 | axum-server = { version = "0.7", features = ["tls-rustls"] } 11 | tokio = { version = "1", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/http-proxy/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-http-proxy" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1", features = ["full"] } 10 | hyper-util = "0.1.1" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower = { version = "0.5.2", features = ["make", "util"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/static-file-server/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-static-file-server" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = { version = "0.5.2", features = ["util"] } 11 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/form/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-form" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | 14 | [dev-dependencies] 15 | http-body-util = "0.1.3" 16 | mime = "0.3.17" 17 | tower = "0.5.2" 18 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/container.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{ 2 | rejection::JsonRejection, 3 | FromRequest, 4 | Json, 5 | }; 6 | use serde::Deserialize; 7 | 8 | #[derive(Deserialize, FromRequest)] 9 | #[from_request(via(Json))] 10 | struct Extractor { 11 | one: i32, 12 | two: String, 13 | three: bool, 14 | } 15 | 16 | fn assert_from_request() 17 | where 18 | Extractor: FromRequest<(), Rejection = JsonRejection>, 19 | { 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_infer_multiple.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | also_inner_state: State, 8 | } 9 | 10 | #[derive(Clone)] 11 | struct AppState {} 12 | 13 | fn assert_from_request() 14 | where 15 | Extractor: axum::extract::FromRequest, 16 | { 17 | } 18 | 19 | fn main() {} 20 | -------------------------------------------------------------------------------- /examples/error-handling/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-error-handling" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tower-http = { version = "0.6.1", features = ["trace"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/mongodb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-mongodb" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | mongodb = "3.3.0" 10 | serde = { version = "1.0", features = ["derive"] } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tower-http = { version = "0.6.1", features = ["add-extension", "trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/container_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{ 2 | rejection::ExtensionRejection, 3 | Extension, 4 | FromRequestParts, 5 | }; 6 | 7 | #[derive(Clone, FromRequestParts)] 8 | #[from_request(via(Extension))] 9 | struct Extractor { 10 | one: i32, 11 | two: String, 12 | three: bool, 13 | } 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: FromRequestParts<(), Rejection = ExtensionRejection>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_field_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::State, routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | fn main() { 5 | let _: axum::Router = Router::new() 6 | .route("/", get(|_: Extractor| async {})) 7 | .with_state(AppState::default()); 8 | } 9 | 10 | #[derive(FromRequest)] 11 | struct Extractor { 12 | #[from_request(via(State))] 13 | state: AppState, 14 | } 15 | 16 | #[derive(Clone, Default)] 17 | struct AppState {} 18 | -------------------------------------------------------------------------------- /examples/prometheus-metrics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-prometheus-metrics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | metrics = { version = "0.24", default-features = false } 10 | metrics-exporter-prometheus = { version = "0.17", default-features = false } 11 | tokio = { version = "1.0", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/reqwest-response/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-reqwest-response" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | reqwest = { version = "0.12", features = ["stream"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tokio-stream = "0.1" 12 | tower-http = { version = "0.6.1", features = ["trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | -------------------------------------------------------------------------------- /examples/unix-domain-socket/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-unix-domain-socket" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1" 10 | hyper = { version = "1.0.0", features = ["full"] } 11 | hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } 12 | tokio = { version = "1.0", features = ["full"] } 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/websockets-http2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-websockets-http2" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["ws", "http2"] } 9 | axum-server = { version = "0.7", features = ["tls-rustls"] } 10 | tokio = { version = "1", features = ["full"] } 11 | tower-http = { version = "0.6", features = ["fs"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /examples/dependency-injection/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-dependency-injection" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["tracing", "macros"] } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tracing = "0.1" 12 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 13 | uuid = { version = "1.0", features = ["serde", "v4"] } 14 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-native-tls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0.0", features = ["full"] } 10 | hyper-util = { version = "0.1" } 11 | tokio = { version = "1", features = ["full"] } 12 | tokio-native-tls = "0.3.1" 13 | tower-service = "0.3.2" 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_ref.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl A { 18 | #[debug_handler] 19 | async fn handler(&self) {} 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/merge.md: -------------------------------------------------------------------------------- 1 | Merge two routers into one. 2 | 3 | This is useful for breaking routers into smaller pieces and combining them 4 | into one. 5 | 6 | ```rust 7 | use axum::{ 8 | routing::{get, post}, 9 | Router, 10 | }; 11 | 12 | let get = get(|| async {}); 13 | let post = post(|| async {}); 14 | 15 | let merged = get.merge(post); 16 | 17 | let app = Router::new().route("/", merged); 18 | 19 | // Our app now accepts 20 | // - GET / 21 | // - POST / 22 | # let _: Router = app; 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/low-level-rustls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-rustls" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0.0", features = ["full"] } 10 | hyper-util = { version = "0.1", features = ["http2"] } 11 | tokio = { version = "1", features = ["full"] } 12 | tokio-rustls = "0.26" 13 | tower-service = "0.3.2" 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extract_self_mut.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl A { 18 | #[debug_handler] 19 | async fn handler(&mut self) {} 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/output_tuple_too_many.stderr: -------------------------------------------------------------------------------- 1 | error: Cannot return tuples with more than 17 elements 2 | --> tests/debug_handler/fail/output_tuple_too_many.rs:4:20 3 | | 4 | 4 | async fn handler() -> ( 5 | | ____________________^ 6 | 5 | | axum::http::StatusCode, 7 | 6 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | 7 | | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 9 | ... | 10 | 23 | | axum::http::StatusCode, 11 | 24 | | ) { 12 | | |_^ 13 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via_infer.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::State, routing::get, Router}; 2 | use axum_macros::FromRequest; 3 | 4 | fn main() { 5 | let _: axum::Router = Router::new() 6 | .route("/b", get(|_: AppState| async {})) 7 | .with_state(AppState::default()); 8 | } 9 | 10 | // if we're extract "via" `State` and not specifying state 11 | // assume `AppState` is the state 12 | #[derive(Clone, Default, FromRequest)] 13 | #[from_request(via(State))] 14 | struct AppState {} 15 | -------------------------------------------------------------------------------- /axum/src/test_helpers/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::disallowed_names)] 2 | 3 | use crate::{extract::Request, response::Response, serve}; 4 | 5 | mod test_client; 6 | pub use self::test_client::*; 7 | 8 | #[cfg(test)] 9 | pub(crate) mod tracing_helpers; 10 | 11 | #[cfg(test)] 12 | pub(crate) mod counting_cloneable_state; 13 | 14 | #[cfg(test)] 15 | pub(crate) fn assert_send() {} 16 | #[cfg(test)] 17 | pub(crate) fn assert_sync() {} 18 | 19 | #[allow(dead_code)] 20 | pub(crate) struct NotSendSync(*const ()); 21 | -------------------------------------------------------------------------------- /examples/stream-to-file/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-stream-to-file" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["multipart"] } 9 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tokio-util = { version = "0.7", features = ["io"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/too_many_extractors.rs: -------------------------------------------------------------------------------- 1 | use axum::http::Uri; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler( 6 | _e1: Uri, 7 | _e2: Uri, 8 | _e3: Uri, 9 | _e4: Uri, 10 | _e5: Uri, 11 | _e6: Uri, 12 | _e7: Uri, 13 | _e8: Uri, 14 | _e9: Uri, 15 | _e10: Uri, 16 | _e11: Uri, 17 | _e12: Uri, 18 | _e13: Uri, 19 | _e14: Uri, 20 | _e15: Uri, 21 | _e16: Uri, 22 | _e17: Uri, 23 | ) { 24 | } 25 | 26 | fn main() {} 27 | -------------------------------------------------------------------------------- /examples/auto-reload/README.md: -------------------------------------------------------------------------------- 1 | # auto-reload 2 | 3 | This example shows how you can set up a development environment for your axum 4 | service such that whenever the source code changes, the app is recompiled and 5 | restarted. It uses `listenfd` to be able to migrate connections from an old 6 | version of the app to a newly-compiled version. 7 | 8 | ## Setup 9 | 10 | ```sh 11 | cargo install cargo-watch systemfd 12 | ``` 13 | 14 | ## Running 15 | 16 | ```sh 17 | systemfd --no-pid -s http::3000 -- cargo watch -x run 18 | ``` 19 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_order.stderr: -------------------------------------------------------------------------------- 1 | error: `Json<_>` consumes the request body and thus must be the last argument to the handler function 2 | --> tests/debug_handler/fail/wrong_order.rs:5:17 3 | | 4 | 5 | async fn one(_: Json<()>, _: Uri) {} 5 | | ^^^^^^^^ 6 | 7 | error: `String` consumes the request body and thus must be the last argument to the handler function 8 | --> tests/debug_handler/fail/wrong_order.rs:8:17 9 | | 10 | 8 | async fn two(_: String, _: Uri) {} 11 | | ^^^^^^ 12 | -------------------------------------------------------------------------------- /examples/jwt/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-jwt" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 10 | jsonwebtoken = { version = "10", features = ["aws_lc_rs"] } 11 | serde = { version = "1.0", features = ["derive"] } 12 | serde_json = "1.0" 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/low-level-openssl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-low-level-openssl" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | hyper = { version = "1.0.0", features = ["full"] } 10 | hyper-util = { version = "0.1" } 11 | openssl = "0.10" 12 | tokio = { version = "1", features = ["full"] } 13 | tokio-openssl = "0.6" 14 | tower = { version = "0.5.2", features = ["make"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/state_infer_multiple_different_types.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::FromRequest; 3 | 4 | #[derive(FromRequest)] 5 | struct Extractor { 6 | inner_state: State, 7 | other_state: State, 8 | } 9 | 10 | #[derive(Clone)] 11 | struct AppState {} 12 | 13 | #[derive(Clone)] 14 | struct OtherState {} 15 | 16 | fn assert_from_request() 17 | where 18 | Extractor: axum::extract::FromRequest, 19 | { 20 | } 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_via.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum::response::Response; 3 | use axum_macros::FromRequest; 4 | use serde::Deserialize; 5 | 6 | #[derive(FromRequest)] 7 | struct Extractor( 8 | #[from_request(via(Query))] Payload, 9 | #[from_request(via(axum::extract::Json))] Payload, 10 | ); 11 | 12 | #[derive(Deserialize)] 13 | struct Payload {} 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: axum::extract::FromRequest<(), Rejection = Response>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/tuple_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | pub type Result = std::result::Result; 5 | 6 | #[derive(TypedPath, Deserialize)] 7 | #[typed_path("/users/{user_id}/teams/{team_id}")] 8 | struct MyPath(u32, u32); 9 | 10 | fn main() { 11 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 12 | 13 | assert_eq!(MyPath::PATH, "/users/{user_id}/teams/{team_id}"); 14 | assert_eq!(format!("{}", MyPath(1, 2)), "/users/1/teams/2"); 15 | } 16 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-customize-extractor-error" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | axum-extra = { path = "../../axum-extra", features = ["with-rejection"] } 10 | serde = { version = "1.0", features = ["derive"] } 11 | serde_json = "1.0" 12 | thiserror = "2" 13 | tokio = { version = "1.20", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /examples/diesel-async-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-diesel-async-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | diesel = "~2.3" 10 | diesel-async = { version = "0.7", features = ["postgres", "bb8", "migrations"] } 11 | diesel_migrations = "~2.3" 12 | serde = { version = "1.0", features = ["derive"] } 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/tuple_same_type_twice_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::Query; 2 | use axum::response::Response; 3 | use axum_macros::FromRequestParts; 4 | use serde::Deserialize; 5 | 6 | #[derive(FromRequestParts)] 7 | struct Extractor( 8 | #[from_request(via(Query))] Payload, 9 | #[from_request(via(axum::extract::Path))] Payload, 10 | ); 11 | 12 | #[derive(Deserialize)] 13 | struct Payload {} 14 | 15 | fn assert_from_request() 16 | where 17 | Extractor: axum::extract::FromRequestParts<(), Rejection = Response>, 18 | { 19 | } 20 | 21 | fn main() {} 22 | -------------------------------------------------------------------------------- /examples/diesel-postgres/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-diesel-postgres" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum", features = ["macros"] } 9 | deadpool-diesel = { version = "0.6.1", features = ["postgres"] } 10 | diesel = { version = "2", features = ["postgres"] } 11 | diesel_migrations = "2" 12 | serde = { version = "1.0", features = ["derive"] } 13 | tokio = { version = "1.0", features = ["full"] } 14 | tracing = "0.1" 15 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 16 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/set_state.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRef, FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler(state = AppState)] 5 | async fn handler(_: A) {} 6 | 7 | #[derive(Clone)] 8 | struct AppState; 9 | 10 | struct A; 11 | 12 | impl FromRequest for A 13 | where 14 | S: Send + Sync, 15 | AppState: FromRef, 16 | { 17 | type Rejection = (); 18 | 19 | async fn from_request(_req: Request, _state: &S) -> Result { 20 | unimplemented!() 21 | } 22 | } 23 | 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/into_uri.rs: -------------------------------------------------------------------------------- 1 | use axum::http::Uri; 2 | use axum_extra::routing::TypedPath; 3 | use serde::Deserialize; 4 | 5 | #[derive(TypedPath, Deserialize)] 6 | #[typed_path("/{id}")] 7 | struct Named { 8 | id: u32, 9 | } 10 | 11 | #[derive(TypedPath, Deserialize)] 12 | #[typed_path("/{id}")] 13 | struct Unnamed(u32); 14 | 15 | #[derive(TypedPath, Deserialize)] 16 | #[typed_path("/")] 17 | struct Unit; 18 | 19 | fn main() { 20 | let _: Uri = Named { id: 1 }.to_uri(); 21 | let _: Uri = Unnamed(1).to_uri(); 22 | let _: Uri = Unit.to_uri(); 23 | } 24 | -------------------------------------------------------------------------------- /examples/todos/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-todos" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | tokio = { version = "1.0", features = ["full"] } 11 | tower = { version = "0.5.2", features = ["util", "timeout"] } 12 | tower-http = { version = "0.6.1", features = ["add-extension", "trace"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | uuid = { version = "1.0", features = ["serde", "v4"] } 16 | -------------------------------------------------------------------------------- /examples/validator/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | edition = "2021" 3 | name = "example-validator" 4 | publish = false 5 | version = "0.1.0" 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde = { version = "1.0", features = ["derive"] } 10 | thiserror = "2" 11 | tokio = { version = "1.0", features = ["full"] } 12 | tracing = "0.1" 13 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 14 | validator = { version = "0.20.0", features = ["derive"] } 15 | 16 | [dev-dependencies] 17 | http-body-util = "0.1.0" 18 | tower = { version = "0.5.2", features = ["util"] } 19 | -------------------------------------------------------------------------------- /axum-macros/tests/from_ref/pass/basic.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | 7 | // This will implement `FromRef` for each field in the struct. 8 | #[derive(Clone, FromRef)] 9 | struct AppState { 10 | auth_token: String, 11 | } 12 | 13 | // So those types can be extracted via `State` 14 | async fn handler(_: State) {} 15 | 16 | fn main() { 17 | let state = AppState { 18 | auth_token: Default::default(), 19 | }; 20 | 21 | let _: axum::Router = Router::new().route("/", get(handler)).with_state(state); 22 | } 23 | -------------------------------------------------------------------------------- /examples/key-value-store/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-key-value-store" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | tokio = { version = "1.0", features = ["full"] } 10 | tower = { version = "0.5.2", features = ["util", "timeout", "load-shed", "limit"] } 11 | tower-http = { version = "0.6.1", features = [ 12 | "add-extension", 13 | "auth", 14 | "compression-full", 15 | "limit", 16 | "trace", 17 | ] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | -------------------------------------------------------------------------------- /examples/compression/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-compression" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | serde_json = "1" 10 | tokio = { version = "1", features = ["macros", "rt-multi-thread"] } 11 | tower = "0.5.2" 12 | tower-http = { version = "0.6.1", features = ["compression-full", "decompression-full"] } 13 | tracing = "0.1" 14 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 15 | 16 | [dev-dependencies] 17 | assert-json-diff = "2.0" 18 | brotli = "8" 19 | flate2 = "1" 20 | http = "1" 21 | zstd = "0.13" 22 | -------------------------------------------------------------------------------- /examples/testing/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-testing" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | http-body-util = "0.1.0" 10 | hyper-util = { version = "0.1", features = ["client", "http1", "client-legacy"] } 11 | mime = "0.3" 12 | serde_json = "1.0" 13 | tokio = { version = "1.0", features = ["full"] } 14 | tower-http = { version = "0.6.1", features = ["trace"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | 18 | [dev-dependencies] 19 | tower = { version = "0.5.2", features = ["util"] } 20 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/url_encoding.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/{param}")] 6 | struct Named { 7 | param: String, 8 | } 9 | 10 | #[derive(TypedPath, Deserialize)] 11 | #[typed_path("/{param}")] 12 | struct Unnamed(String); 13 | 14 | fn main() { 15 | assert_eq!( 16 | format!( 17 | "{}", 18 | Named { 19 | param: "a b".to_string() 20 | } 21 | ), 22 | "/a%20b" 23 | ); 24 | 25 | assert_eq!(format!("{}", Unnamed("a b".to_string()),), "/a%20b"); 26 | } 27 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/infer_state.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::State; 2 | use axum_macros::debug_handler; 3 | 4 | #[debug_handler] 5 | async fn handler(_: State) {} 6 | 7 | #[debug_handler] 8 | async fn handler_2(_: axum::extract::State) {} 9 | 10 | #[debug_handler] 11 | async fn handler_3(_: axum::extract::State, _: axum::extract::State) {} 12 | 13 | #[debug_handler] 14 | async fn handler_4(_: State, _: State) {} 15 | 16 | #[debug_handler] 17 | async fn handler_5(_: axum::extract::State, _: State) {} 18 | 19 | #[derive(Clone)] 20 | struct AppState; 21 | 22 | fn main() {} 23 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_cookie.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::FromRef; 2 | use axum_extra::extract::cookie::{Key, PrivateCookieJar}; 3 | use axum_macros::FromRequest; 4 | 5 | #[derive(FromRequest)] 6 | #[from_request(state(AppState))] 7 | struct Extractor { 8 | cookies: PrivateCookieJar, 9 | } 10 | 11 | struct AppState { 12 | key: Key, 13 | } 14 | 15 | impl FromRef for Key { 16 | fn from_ref(input: &AppState) -> Self { 17 | input.key.clone() 18 | } 19 | } 20 | 21 | fn assert_from_request() 22 | where 23 | Extractor: axum::extract::FromRequest, 24 | { 25 | } 26 | 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/README.md: -------------------------------------------------------------------------------- 1 | This example explores 3 different ways you can create custom rejections for 2 | already existing extractors 3 | 4 | - [`with_rejection`](src/with_rejection.rs): Uses 5 | `axum_extra::extract::WithRejection` to transform one rejection into another 6 | - [`derive_from_request`](src/derive_from_request.rs): Uses the 7 | `axum::extract::FromRequest` derive macro to wrap another extractor and 8 | customize the rejection 9 | - [`custom_extractor`](src/custom_extractor.rs): Manual implementation of 10 | `FromRequest` that wraps another extractor 11 | 12 | Run with 13 | 14 | ```sh 15 | cargo run -p example-customize-extractor-error 16 | ``` 17 | -------------------------------------------------------------------------------- /.github/DISCUSSION_TEMPLATE/q-a.yml: -------------------------------------------------------------------------------- 1 | body: 2 | - type: textarea 3 | attributes: 4 | label: Summary 5 | description: 'Your question:' 6 | validations: 7 | required: true 8 | - type: input 9 | attributes: 10 | label: axum version 11 | description: 'Please look it up in `Cargo.lock`, or as described below' 12 | validations: 13 | required: true 14 | - type: markdown 15 | attributes: 16 | value: | 17 | > If you have `jq` installed, you can look up the version by running 18 | > 19 | > ```bash 20 | > cargo metadata --format-version=1 | jq -r '.packages[] | select(.name == "axum") | .version' 21 | > ``` 22 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ## Motivation 11 | 12 | 17 | 18 | ## Solution 19 | 20 | 24 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/fail/missing_capture.stderr: -------------------------------------------------------------------------------- 1 | error[E0027]: pattern does not mention field `id` 2 | --> tests/typed_path/fail/missing_capture.rs:5:14 3 | | 4 | 5 | #[typed_path("/users")] 5 | | ^^^^^^^^ missing field `id` 6 | | 7 | help: include the missing field in the pattern 8 | | 9 | 5 | #[typed_path("/users" { id })] 10 | | ++++++ 11 | help: if you don't care about this missing field, you can explicitly ignore it 12 | | 13 | 5 | #[typed_path("/users" { id: _ })] 14 | | +++++++++ 15 | help: or always ignore missing fields here 16 | | 17 | 5 | #[typed_path("/users" { .. })] 18 | | ++++++ 19 | -------------------------------------------------------------------------------- /examples/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-hello-world 5 | //! ``` 6 | 7 | use axum::{response::Html, routing::get, Router}; 8 | 9 | #[tokio::main] 10 | async fn main() { 11 | // build our application with a route 12 | let app = Router::new().route("/", get(handler)); 13 | 14 | // run it 15 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 16 | .await 17 | .unwrap(); 18 | println!("listening on {}", listener.local_addr().unwrap()); 19 | axum::serve(listener, app).await.unwrap(); 20 | } 21 | 22 | async fn handler() -> Html<&'static str> { 23 | Html("

Hello, World!

") 24 | } 25 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequestParts, response::Response}; 2 | use axum_extra::{ 3 | headers::{self, UserAgent}, 4 | typed_header::TypedHeaderRejection, 5 | TypedHeader, 6 | }; 7 | 8 | #[derive(FromRequestParts)] 9 | struct Extractor { 10 | uri: axum::http::Uri, 11 | user_agent: TypedHeader, 12 | content_type: TypedHeader, 13 | etag: Option>, 14 | host: Result, TypedHeaderRejection>, 15 | } 16 | 17 | fn assert_from_request() 18 | where 19 | Extractor: FromRequestParts<(), Rejection = Response>, 20 | { 21 | } 22 | 23 | fn main() {} 24 | -------------------------------------------------------------------------------- /examples/oauth/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-oauth" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | async-session = "3.0.0" 10 | axum = { path = "../../axum" } 11 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 12 | http = "1.0.0" 13 | oauth2 = "5" 14 | # Use Rustls because it makes it easier to cross-compile on CI 15 | reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json"] } 16 | serde = { version = "1.0", features = ["derive"] } 17 | tokio = { version = "1.0", features = ["full"] } 18 | tracing = "0.1" 19 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 20 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_request_consumers.stderr: -------------------------------------------------------------------------------- 1 | error: Can't have two extractors that consume the request body. `Json<_>` and `String` both do that. 2 | --> tests/debug_handler/fail/multiple_request_consumers.rs:9:14 3 | | 4 | 9 | async fn one(_: Json<()>, _: String, _: Uri) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 6 | 7 | error: Can't have more than one extractor that consume the request body. `Json<_>`, `Bytes`, and `String` all do that. 8 | --> tests/debug_handler/fail/multiple_request_consumers.rs:12:14 9 | | 10 | 12 | async fn two(_: Json<()>, _: Method, _: Bytes, _: Uri, _: String) {} 11 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::FromRequest, response::Response}; 2 | use axum_extra::{ 3 | headers::{self, UserAgent}, 4 | typed_header::TypedHeaderRejection, 5 | TypedHeader, 6 | }; 7 | 8 | #[derive(FromRequest)] 9 | struct Extractor { 10 | uri: axum::http::Uri, 11 | user_agent: TypedHeader, 12 | content_type: TypedHeader, 13 | etag: Option>, 14 | host: Result, TypedHeaderRejection>, 15 | body: String, 16 | } 17 | 18 | fn assert_from_request() 19 | where 20 | Extractor: FromRequest<(), Rejection = Response>, 21 | { 22 | } 23 | 24 | fn main() {} 25 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/multiple_paths.stderr: -------------------------------------------------------------------------------- 1 | error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`, not by applying multiple `Path<_>` extractors 2 | --> tests/debug_handler/fail/multiple_paths.rs:5:18 3 | | 4 | 5 | async fn handler(_: Path, _: Path) {} 5 | | ^^^^^^^^^^^^^^^ 6 | 7 | error: Multiple parameters must be extracted with a tuple `Path<(_, _)>` or a struct `Path`, not by applying multiple `Path<_>` extractors 8 | --> tests/debug_handler/fail/multiple_paths.rs:5:35 9 | | 10 | 5 | async fn handler(_: Path, _: Path) {} 11 | | ^^^^^^^^^^^^^^^ 12 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/named_fields_struct.rs: -------------------------------------------------------------------------------- 1 | use axum_extra::routing::TypedPath; 2 | use serde::Deserialize; 3 | 4 | #[derive(TypedPath, Deserialize)] 5 | #[typed_path("/users/{user_id}/teams/{team_id}")] 6 | struct MyPath { 7 | user_id: u32, 8 | team_id: u32, 9 | } 10 | 11 | fn main() { 12 | _ = axum::Router::<()>::new().route("/", axum::routing::get(|_: MyPath| async {})); 13 | 14 | assert_eq!(MyPath::PATH, "/users/{user_id}/teams/{team_id}"); 15 | assert_eq!( 16 | format!( 17 | "{}", 18 | MyPath { 19 | user_id: 1, 20 | team_id: 2 21 | } 22 | ), 23 | "/users/1/teams/2" 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/result_handler.rs: -------------------------------------------------------------------------------- 1 | use axum::{extract::rejection::PathRejection, http::StatusCode}; 2 | use axum_extra::routing::{RouterExt, TypedPath}; 3 | use serde::Deserialize; 4 | 5 | #[derive(TypedPath, Deserialize)] 6 | #[typed_path("/users/{id}")] 7 | struct UsersShow { 8 | id: String, 9 | } 10 | 11 | async fn result_handler(_: Result) {} 12 | 13 | #[derive(TypedPath, Deserialize)] 14 | #[typed_path("/users")] 15 | struct UsersIndex; 16 | 17 | async fn result_handler_unit_struct(_: Result) {} 18 | 19 | fn main() { 20 | _ = axum::Router::<()>::new() 21 | .typed_post(result_handler) 22 | .typed_post(result_handler_unit_struct); 23 | } 24 | -------------------------------------------------------------------------------- /axum-extra/src/extract/rejection.rs: -------------------------------------------------------------------------------- 1 | //! Rejection response types. 2 | 3 | use axum_core::{ 4 | __composite_rejection as composite_rejection, __define_rejection as define_rejection, 5 | }; 6 | 7 | define_rejection! { 8 | #[status = BAD_REQUEST] 9 | #[body = "No host found in request"] 10 | /// Rejection type used if the [`Host`](super::Host) extractor is unable to 11 | /// resolve a host. 12 | pub struct FailedToResolveHost; 13 | } 14 | 15 | composite_rejection! { 16 | /// Rejection used for [`Host`](super::Host). 17 | /// 18 | /// Contains one variant for each way the [`Host`](super::Host) extractor 19 | /// can fail. 20 | pub enum HostRejection { 21 | FailedToResolveHost, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature Request 3 | about: I have a suggestion (and may want to implement it 🙂)! 4 | --- 5 | 6 | - [ ] I have looked for existing issues (including closed) about this 7 | 8 | ## Feature Request 9 | 10 | ### Motivation 11 | 12 | 15 | 16 | ### Proposal 17 | 18 | 22 | 23 | ### Alternatives 24 | 25 | 30 | -------------------------------------------------------------------------------- /examples/simple-router-wasm/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-simple-router-wasm" 3 | version = "0.1.0" 4 | edition = "2018" 5 | publish = false 6 | 7 | [package.metadata.cargo-machete] 8 | ignored = ["axum-extra"] 9 | 10 | [dependencies] 11 | # `default-features = false` to not depend on tokio features which don't support wasm 12 | # you can still pull in tokio manually and only add features that tokio supports for wasm 13 | axum = { path = "../../axum", default-features = false } 14 | # we don't strictly use axum-extra in this example but wanna make sure that 15 | # works in wasm as well 16 | axum-extra = { path = "../../axum-extra", default-features = false } 17 | futures-executor = "0.3.21" 18 | http = "1.0.0" 19 | tower-service = "0.3.1" 20 | -------------------------------------------------------------------------------- /examples/sse/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-sse" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [dependencies] 8 | axum = { path = "../../axum" } 9 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 10 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 11 | headers = "0.4" 12 | tokio = { version = "1.0", features = ["full"] } 13 | tokio-stream = "0.1" 14 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 15 | tracing = "0.1" 16 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 17 | 18 | [dev-dependencies] 19 | eventsource-stream = "0.2" 20 | reqwest = { version = "0.12", features = ["stream"] } 21 | reqwest-eventsource = "0.6" 22 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_tuple.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | #[axum::debug_handler] 4 | async fn named_type() -> ( 5 | axum::http::StatusCode, 6 | axum::Json<&'static str>, 7 | axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | ) { 9 | panic!() 10 | } 11 | 12 | struct CustomIntoResponse {} 13 | impl axum::response::IntoResponse for CustomIntoResponse { 14 | fn into_response(self) -> axum::response::Response { 15 | todo!() 16 | } 17 | } 18 | #[axum::debug_handler] 19 | async fn custom_type() -> ( 20 | axum::http::StatusCode, 21 | CustomIntoResponse, 22 | axum::response::AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 23 | ) { 24 | panic!() 25 | } 26 | 27 | fn main() {} 28 | -------------------------------------------------------------------------------- /examples/websockets/assets/script.js: -------------------------------------------------------------------------------- 1 | const socket = new WebSocket('ws://localhost:3000/ws'); 2 | 3 | socket.addEventListener('open', function (event) { 4 | socket.send('Hello Server!'); 5 | }); 6 | 7 | socket.addEventListener('message', function (event) { 8 | console.log('Message from server ', event.data); 9 | }); 10 | 11 | 12 | setTimeout(() => { 13 | const obj = { hello: "world" }; 14 | const blob = new Blob([JSON.stringify(obj, null, 2)], { 15 | type: "application/json", 16 | }); 17 | console.log("Sending blob over websocket"); 18 | socket.send(blob); 19 | }, 1000); 20 | 21 | setTimeout(() => { 22 | socket.send('About done here...'); 23 | console.log("Sending close over websocket"); 24 | socket.close(3000, "Crash and Burn!"); 25 | }, 3000); -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [graph] 2 | exclude-unpublished = true 3 | 4 | [advisories] 5 | unmaintained = "none" 6 | ignore = [] 7 | 8 | [licenses] 9 | confidence-threshold = 0.8 10 | allow = [ 11 | "Apache-2.0", 12 | "BSD-3-Clause", 13 | "MIT", 14 | "Unicode-3.0", 15 | ] 16 | 17 | [bans] 18 | multiple-versions = "deny" 19 | highlight = "all" 20 | skip-tree = [ 21 | # currently duplicated through header, reqwest, tower-http and cookie 22 | # C.f. https://github.com/tokio-rs/axum/pull/1641 23 | { name = "base64" }, 24 | # parking_lot pulls in old versions of windows-sys 25 | { name = "windows-sys" }, 26 | # pulled in by quickcheck and cookie 27 | { name = "rand" }, 28 | ] 29 | 30 | [sources] 31 | unknown-registry = "warn" 32 | unknown-git = "warn" 33 | allow-git = [] 34 | -------------------------------------------------------------------------------- /examples/websockets/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "example-websockets" 3 | version = "0.1.0" 4 | edition = "2021" 5 | publish = false 6 | 7 | [[bin]] 8 | name = "example-websockets" 9 | path = "src/main.rs" 10 | 11 | [[bin]] 12 | name = "example-client" 13 | path = "src/client.rs" 14 | 15 | [dependencies] 16 | axum = { path = "../../axum", features = ["ws"] } 17 | axum-extra = { path = "../../axum-extra", features = ["typed-header"] } 18 | futures-util = { version = "0.3", default-features = false, features = ["sink", "std"] } 19 | headers = "0.4" 20 | tokio = { version = "1.0", features = ["full"] } 21 | tokio-tungstenite = "0.28.0" 22 | tower-http = { version = "0.6.1", features = ["fs", "trace"] } 23 | tracing = "0.1" 24 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 25 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/b", get(|_: (), _: AppState| async {})) 11 | .route("/c", get(|_: (), _: InnerState| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(Clone, Default, FromRequest)] 16 | #[from_request(via(State), state(AppState))] 17 | struct AppState { 18 | inner: InnerState, 19 | } 20 | 21 | #[derive(Clone, Default, FromRequest)] 22 | #[from_request(via(State), state(AppState))] 23 | struct InnerState {} 24 | 25 | impl FromRef for InnerState { 26 | fn from_ref(input: &AppState) -> Self { 27 | input.inner.clone() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/pass/self_receiver.rs: -------------------------------------------------------------------------------- 1 | use axum::extract::{FromRequest, Request}; 2 | use axum_macros::debug_handler; 3 | 4 | struct A; 5 | 6 | impl FromRequest for A 7 | where 8 | S: Send + Sync, 9 | { 10 | type Rejection = (); 11 | 12 | async fn from_request(_req: Request, _state: &S) -> Result { 13 | unimplemented!() 14 | } 15 | } 16 | 17 | impl FromRequest for Box 18 | where 19 | S: Send + Sync, 20 | { 21 | type Rejection = (); 22 | 23 | async fn from_request(_req: Request, _state: &S) -> Result { 24 | unimplemented!() 25 | } 26 | } 27 | 28 | impl A { 29 | #[debug_handler] 30 | async fn handler(self) {} 31 | 32 | #[debug_handler] 33 | async fn handler_with_qualified_self(self: Box) {} 34 | } 35 | 36 | fn main() {} 37 | -------------------------------------------------------------------------------- /examples/compression/README.md: -------------------------------------------------------------------------------- 1 | # compression 2 | 3 | This example shows how to: 4 | - automatically decompress request bodies when necessary 5 | - compress response bodies based on the `accept` header. 6 | 7 | ## Running 8 | 9 | ``` 10 | cargo run -p example-compression 11 | ``` 12 | 13 | ## Sending compressed requests 14 | 15 | ``` 16 | curl -v -g 'http://localhost:3000/' \ 17 | -H "Content-Type: application/json" \ 18 | -H "Content-Encoding: gzip" \ 19 | --compressed \ 20 | --data-binary @data/products.json.gz 21 | ``` 22 | 23 | (Notice the `Content-Encoding: gzip` in the request, and `content-encoding: gzip` in the response.) 24 | 25 | ## Sending uncompressed requests 26 | 27 | ``` 28 | curl -v -g 'http://localhost:3000/' \ 29 | -H "Content-Type: application/json" \ 30 | --compressed \ 31 | --data-binary @data/products.json 32 | ``` 33 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_enum_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState| async {})) 11 | .route("/b", get(|_: InnerState| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(Clone, FromRequest)] 16 | #[from_request(via(State))] 17 | enum AppState { 18 | One, 19 | } 20 | 21 | impl Default for AppState { 22 | fn default() -> AppState { 23 | Self::One 24 | } 25 | } 26 | 27 | #[derive(FromRequest)] 28 | #[from_request(via(State), state(AppState))] 29 | enum InnerState {} 30 | 31 | impl FromRef for InnerState { 32 | fn from_ref(_: &AppState) -> Self { 33 | todo!("🤷") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_field_explicit.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/", get(|_: Extractor| async {})) 11 | .with_state(AppState::default()); 12 | } 13 | 14 | #[derive(FromRequest)] 15 | #[from_request(state(AppState))] 16 | struct Extractor { 17 | #[from_request(via(State))] 18 | state: AppState, 19 | #[from_request(via(State))] 20 | inner: InnerState, 21 | } 22 | 23 | #[derive(Clone, Default)] 24 | struct AppState { 25 | inner: InnerState, 26 | } 27 | 28 | #[derive(Clone, Default)] 29 | struct InnerState {} 30 | 31 | impl FromRef for InnerState { 32 | fn from_ref(input: &AppState) -> Self { 33 | input.inner.clone() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_explicit_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, Query, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | use std::collections::HashMap; 8 | 9 | fn main() { 10 | let _: axum::Router = Router::new() 11 | .route("/b", get(|_: Extractor| async {})) 12 | .with_state(AppState::default()); 13 | } 14 | 15 | #[derive(FromRequestParts)] 16 | #[from_request(state(AppState))] 17 | struct Extractor { 18 | inner_state: State, 19 | other: Query>, 20 | } 21 | 22 | #[derive(Default, Clone)] 23 | struct AppState { 24 | inner: InnerState, 25 | } 26 | 27 | #[derive(Clone, Default)] 28 | struct InnerState {} 29 | 30 | impl FromRef for InnerState { 31 | fn from_ref(input: &AppState) -> Self { 32 | input.inner.clone() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/override_rejection_on_enum_without_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest, Clone)] 18 | #[from_request(rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_via.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{Extension, FromRequest}, 3 | response::Response, 4 | }; 5 | use axum_extra::{ 6 | headers::{self, UserAgent}, 7 | typed_header::TypedHeaderRejection, 8 | TypedHeader, 9 | }; 10 | 11 | #[derive(FromRequest)] 12 | struct Extractor { 13 | #[from_request(via(Extension))] 14 | state: State, 15 | #[from_request(via(TypedHeader))] 16 | user_agent: UserAgent, 17 | #[from_request(via(TypedHeader))] 18 | content_type: headers::ContentType, 19 | #[from_request(via(TypedHeader))] 20 | etag: Option, 21 | #[from_request(via(TypedHeader))] 22 | host: Result, 23 | } 24 | 25 | fn assert_from_request() 26 | where 27 | Extractor: FromRequest<(), Rejection = Response>, 28 | { 29 | } 30 | 31 | #[derive(Clone)] 32 | struct State; 33 | 34 | fn main() {} 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_enum.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest, Clone)] 18 | #[from_request(via(axum::Extension), rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-core/src/error.rs: -------------------------------------------------------------------------------- 1 | use crate::BoxError; 2 | use std::{error::Error as StdError, fmt}; 3 | 4 | /// Errors that can happen when using axum. 5 | #[derive(Debug)] 6 | pub struct Error { 7 | inner: BoxError, 8 | } 9 | 10 | impl Error { 11 | /// Create a new `Error` from a boxable error. 12 | pub fn new(error: impl Into) -> Self { 13 | Self { 14 | inner: error.into(), 15 | } 16 | } 17 | 18 | /// Convert an `Error` back into the underlying boxed trait object. 19 | #[must_use] 20 | pub fn into_inner(self) -> BoxError { 21 | self.inner 22 | } 23 | } 24 | 25 | impl fmt::Display for Error { 26 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 27 | self.inner.fmt(f) 28 | } 29 | } 30 | 31 | impl StdError for Error { 32 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 33 | Some(&*self.inner) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState, _: InnerState, _: String| async {})) 11 | .route("/b", get(|_: AppState, _: String| async {})) 12 | .route("/c", get(|_: InnerState, _: String| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, Default, FromRequestParts)] 17 | #[from_request(via(State))] 18 | struct AppState { 19 | inner: InnerState, 20 | } 21 | 22 | #[derive(Clone, Default, FromRequestParts)] 23 | #[from_request(via(State), state(AppState))] 24 | struct InnerState {} 25 | 26 | impl FromRef for InnerState { 27 | fn from_ref(input: &AppState) -> Self { 28 | input.inner.clone() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to all routes in the router. 2 | 3 | This can be used to add additional processing to a request for a group 4 | of routes. 5 | 6 | Note that the middleware is only applied to existing routes. So you have to 7 | first add your routes (and / or fallback) and then call `layer` afterwards. Additional 8 | routes added after `layer` is called will not have the middleware added. 9 | 10 | Works similarly to [`Router::layer`](super::Router::layer). See that method for 11 | more details. 12 | 13 | # Example 14 | 15 | ```rust 16 | use axum::{routing::get, Router}; 17 | use tower::limit::ConcurrencyLimitLayer; 18 | 19 | async fn handler() {} 20 | 21 | let app = Router::new().route( 22 | "/", 23 | // All requests to `GET /` will be sent through `ConcurrencyLimitLayer` 24 | get(handler).layer(ConcurrencyLimitLayer::new(64)), 25 | ); 26 | # let _: Router = app; 27 | ``` 28 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/named_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{Extension, FromRequestParts}, 3 | response::Response, 4 | }; 5 | use axum_extra::{ 6 | headers::{self, UserAgent}, 7 | typed_header::TypedHeaderRejection, 8 | TypedHeader, 9 | }; 10 | 11 | #[derive(FromRequestParts)] 12 | struct Extractor { 13 | #[from_request(via(Extension))] 14 | state: State, 15 | #[from_request(via(TypedHeader))] 16 | user_agent: UserAgent, 17 | #[from_request(via(TypedHeader))] 18 | content_type: headers::ContentType, 19 | #[from_request(via(TypedHeader))] 20 | etag: Option, 21 | #[from_request(via(TypedHeader))] 22 | host: Result, 23 | } 24 | 25 | fn assert_from_request() 26 | where 27 | Extractor: FromRequestParts<(), Rejection = Response>, 28 | { 29 | } 30 | 31 | #[derive(Clone)] 32 | struct State; 33 | 34 | fn main() {} 35 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_enum_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::ExtensionRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequestParts, Clone)] 18 | #[from_request(via(axum::Extension), rejection(MyRejection))] 19 | enum MyExtractor {} 20 | 21 | struct MyRejection {} 22 | 23 | impl From for MyRejection { 24 | fn from(_: ExtensionRejection) -> Self { 25 | todo!() 26 | } 27 | } 28 | 29 | impl IntoResponse for MyRejection { 30 | fn into_response(self) -> Response { 31 | todo!() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/wrong_return_type.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `bool: IntoResponse` is not satisfied 2 | --> tests/debug_handler/fail/wrong_return_type.rs:4:23 3 | | 4 | 4 | async fn handler() -> bool { 5 | | ^^^^ the trait `IntoResponse` is not implemented for `bool` 6 | | 7 | = help: the following other types implement trait `IntoResponse`: 8 | &'static [u8; N] 9 | &'static [u8] 10 | &'static str 11 | () 12 | (R,) 13 | (Response<()>, R) 14 | (Response<()>, T1, R) 15 | (Response<()>, T1, T2, R) 16 | and $N others 17 | note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` 18 | --> tests/debug_handler/fail/wrong_return_type.rs:4:23 19 | | 20 | 4 | async fn handler() -> bool { 21 | | ^^^^ required by this bound in `check` 22 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_with_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::State, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use std::convert::Infallible; 9 | 10 | fn main() { 11 | let _: axum::Router = Router::new() 12 | .route("/a", get(|_: Extractor| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, Default, FromRequest)] 17 | #[from_request(rejection(MyRejection))] 18 | struct Extractor { 19 | state: State, 20 | } 21 | 22 | #[derive(Clone, Default)] 23 | struct AppState {} 24 | 25 | struct MyRejection {} 26 | 27 | impl From for MyRejection { 28 | fn from(err: Infallible) -> Self { 29 | match err {} 30 | } 31 | } 32 | 33 | impl IntoResponse for MyRejection { 34 | fn into_response(self) -> Response { 35 | ().into_response() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_enum_via_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequestParts; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/a", get(|_: AppState| async {})) 11 | .route("/b", get(|_: InnerState| async {})) 12 | .route("/c", get(|_: AppState, _: InnerState| async {})) 13 | .with_state(AppState::default()); 14 | } 15 | 16 | #[derive(Clone, FromRequestParts)] 17 | #[from_request(via(State))] 18 | enum AppState { 19 | One, 20 | } 21 | 22 | impl Default for AppState { 23 | fn default() -> AppState { 24 | Self::One 25 | } 26 | } 27 | 28 | #[derive(FromRequestParts)] 29 | #[from_request(via(State), state(AppState))] 30 | enum InnerState {} 31 | 32 | impl FromRef for InnerState { 33 | fn from_ref(_: &AppState) -> Self { 34 | todo!("🤷") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum-core/src/extract/from_ref.rs: -------------------------------------------------------------------------------- 1 | /// Used to do reference-to-value conversions thus not consuming the input value. 2 | /// 3 | /// This is mainly used with [`State`] to extract "substates" from a reference to main application 4 | /// state. 5 | /// 6 | /// See [`State`] for more details on how library authors should use this trait. 7 | /// 8 | /// This trait can be derived using `#[derive(FromRef)]`. 9 | /// 10 | /// [`State`]: https://docs.rs/axum/0.8/axum/extract/struct.State.html 11 | // NOTE: This trait is defined in axum-core, even though it is mainly used with `State` which is 12 | // defined in axum. That allows crate authors to use it when implementing extractors. 13 | pub trait FromRef { 14 | /// Converts to this type from a reference to the input type. 15 | fn from_ref(input: &T) -> Self; 16 | } 17 | 18 | impl FromRef for T 19 | where 20 | T: Clone, 21 | { 22 | fn from_ref(input: &T) -> Self { 23 | input.clone() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/not_send.stderr: -------------------------------------------------------------------------------- 1 | error: future cannot be sent between threads safely 2 | --> tests/debug_handler/fail/not_send.rs:3:1 3 | | 4 | 3 | #[debug_handler] 5 | | ^^^^^^^^^^^^^^^^ future returned by `handler` is not `Send` 6 | | 7 | = help: within `impl Future`, the trait `Send` is not implemented for `Rc<()>` 8 | note: future is not `Send` as this value is used across an await 9 | --> tests/debug_handler/fail/not_send.rs:6:14 10 | | 11 | 5 | let _rc = std::rc::Rc::new(()); 12 | | --- has type `Rc<()>` which is not `Send` 13 | 6 | async {}.await; 14 | | ^^^^^ await occurs here, with `_rc` maybe used later 15 | note: required by a bound in `check` 16 | --> tests/debug_handler/fail/not_send.rs:3:1 17 | | 18 | 3 | #[debug_handler] 19 | | ^^^^^^^^^^^^^^^^ required by this bound in `check` 20 | = note: this error originates in the attribute macro `debug_handler` (in Nightly builds, run with -Z macro-backtrace for more info) 21 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_struct.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::JsonRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use serde::Deserialize; 9 | 10 | fn main() { 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 12 | } 13 | 14 | #[derive(Deserialize)] 15 | struct Payload {} 16 | 17 | async fn handler(_: MyJson) {} 18 | 19 | async fn handler_result(_: Result, MyJsonRejection>) {} 20 | 21 | #[derive(FromRequest)] 22 | #[from_request(via(axum::Json), rejection(MyJsonRejection))] 23 | struct MyJson(T); 24 | 25 | struct MyJsonRejection {} 26 | 27 | impl From for MyJsonRejection { 28 | fn from(_: JsonRejection) -> Self { 29 | todo!() 30 | } 31 | } 32 | 33 | impl IntoResponse for MyJsonRejection { 34 | fn into_response(self) -> Response { 35 | todo!() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_non_generic.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::JsonRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequest; 8 | use serde::Deserialize; 9 | use std::collections::HashMap; 10 | 11 | fn main() { 12 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 13 | } 14 | 15 | async fn handler(_: MyJson) {} 16 | 17 | async fn handler_result(_: Result) {} 18 | 19 | #[derive(FromRequest, Deserialize)] 20 | #[from_request(via(axum::extract::Json), rejection(MyJsonRejection))] 21 | #[serde(transparent)] 22 | struct MyJson(HashMap); 23 | 24 | struct MyJsonRejection {} 25 | 26 | impl From for MyJsonRejection { 27 | fn from(_: JsonRejection) -> Self { 28 | todo!() 29 | } 30 | } 31 | 32 | impl IntoResponse for MyJsonRejection { 33 | fn into_response(self) -> Response { 34 | todo!() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum/src/extract/raw_query.rs: -------------------------------------------------------------------------------- 1 | use super::FromRequestParts; 2 | use http::request::Parts; 3 | use std::convert::Infallible; 4 | 5 | /// Extractor that extracts the raw query string, without parsing it. 6 | /// 7 | /// # Example 8 | /// 9 | /// ```rust,no_run 10 | /// use axum::{ 11 | /// extract::RawQuery, 12 | /// routing::get, 13 | /// Router, 14 | /// }; 15 | /// use futures_util::StreamExt; 16 | /// 17 | /// async fn handler(RawQuery(query): RawQuery) { 18 | /// // ... 19 | /// } 20 | /// 21 | /// let app = Router::new().route("/users", get(handler)); 22 | /// # let _: Router = app; 23 | /// ``` 24 | #[derive(Debug)] 25 | pub struct RawQuery(pub Option); 26 | 27 | impl FromRequestParts for RawQuery 28 | where 29 | S: Send + Sync, 30 | { 31 | type Rejection = Infallible; 32 | 33 | async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result { 34 | let query = parts.uri.query().map(|query| query.to_owned()); 35 | Ok(Self(query)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_with_via_on_struct_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::QueryRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | use serde::Deserialize; 9 | 10 | fn main() { 11 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 12 | } 13 | 14 | #[derive(Deserialize)] 15 | struct Payload {} 16 | 17 | async fn handler(_: MyQuery) {} 18 | 19 | async fn handler_result(_: Result, MyQueryRejection>) {} 20 | 21 | #[derive(FromRequestParts)] 22 | #[from_request(via(axum::extract::Query), rejection(MyQueryRejection))] 23 | struct MyQuery(T); 24 | 25 | struct MyQueryRejection {} 26 | 27 | impl From for MyQueryRejection { 28 | fn from(_: QueryRejection) -> Self { 29 | todo!() 30 | } 31 | } 32 | 33 | impl IntoResponse for MyQueryRejection { 34 | fn into_response(self) -> Response { 35 | todo!() 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_non_generic_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::QueryRejection, 3 | response::{IntoResponse, Response}, 4 | routing::get, 5 | Router, 6 | }; 7 | use axum_macros::FromRequestParts; 8 | use serde::Deserialize; 9 | use std::collections::HashMap; 10 | 11 | fn main() { 12 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 13 | } 14 | 15 | async fn handler(_: MyQuery) {} 16 | 17 | async fn handler_result(_: Result) {} 18 | 19 | #[derive(FromRequestParts, Deserialize)] 20 | #[from_request(via(axum::extract::Query), rejection(MyQueryRejection))] 21 | #[serde(transparent)] 22 | struct MyQuery(HashMap); 23 | 24 | struct MyQueryRejection {} 25 | 26 | impl From for MyQueryRejection { 27 | fn from(_: QueryRejection) -> Self { 28 | todo!() 29 | } 30 | } 31 | 32 | impl IntoResponse for MyQueryRejection { 33 | fn into_response(self) -> Response { 34 | todo!() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/state_explicit.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{FromRef, State}, 3 | routing::get, 4 | Router, 5 | }; 6 | use axum_macros::FromRequest; 7 | 8 | fn main() { 9 | let _: axum::Router = Router::new() 10 | .route("/b", get(|_: Extractor| async {})) 11 | .with_state(AppState::default()); 12 | } 13 | 14 | #[derive(FromRequest)] 15 | #[from_request(state(AppState))] 16 | struct Extractor { 17 | app_state: State, 18 | one: State, 19 | two: State, 20 | other_extractor: String, 21 | } 22 | 23 | #[derive(Clone, Default)] 24 | struct AppState { 25 | one: One, 26 | two: Two, 27 | } 28 | 29 | #[derive(Clone, Default)] 30 | struct One {} 31 | 32 | impl FromRef for One { 33 | fn from_ref(input: &AppState) -> Self { 34 | input.one.clone() 35 | } 36 | } 37 | 38 | #[derive(Clone, Default)] 39 | struct Two {} 40 | 41 | impl FromRef for Two { 42 | fn from_ref(input: &AppState) -> Self { 43 | input.two.clone() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /axum/src/routing/not_found.rs: -------------------------------------------------------------------------------- 1 | use crate::response::Response; 2 | use axum_core::response::IntoResponse; 3 | use http::{Request, StatusCode}; 4 | use std::{ 5 | convert::Infallible, 6 | future::ready, 7 | task::{Context, Poll}, 8 | }; 9 | use tower_service::Service; 10 | 11 | /// A [`Service`] that responds with `404 Not Found` to all requests. 12 | /// 13 | /// This is used as the bottom service in a method router. You shouldn't have to 14 | /// use it manually. 15 | #[derive(Clone, Copy, Debug)] 16 | pub(super) struct NotFound; 17 | 18 | impl Service> for NotFound 19 | where 20 | B: Send + 'static, 21 | { 22 | type Response = Response; 23 | type Error = Infallible; 24 | type Future = std::future::Ready>; 25 | 26 | #[inline] 27 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { 28 | Poll::Ready(Ok(())) 29 | } 30 | 31 | fn call(&mut self, _req: Request) -> Self::Future { 32 | ready(Ok(StatusCode::NOT_FOUND.into_response())) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/auto-reload/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p auto-reload 5 | //! ``` 6 | 7 | use axum::{response::Html, routing::get, Router}; 8 | use listenfd::ListenFd; 9 | use tokio::net::TcpListener; 10 | 11 | #[tokio::main] 12 | async fn main() { 13 | // build our application with a route 14 | let app = Router::new().route("/", get(handler)); 15 | 16 | let mut listenfd = ListenFd::from_env(); 17 | let listener = match listenfd.take_tcp_listener(0).unwrap() { 18 | // if we are given a tcp listener on listen fd 0, we use that one 19 | Some(listener) => { 20 | listener.set_nonblocking(true).unwrap(); 21 | TcpListener::from_std(listener).unwrap() 22 | } 23 | // otherwise fall back to local listening 24 | None => TcpListener::bind("127.0.0.1:3000").await.unwrap(), 25 | }; 26 | 27 | // run it 28 | println!("listening on {}", listener.local_addr().unwrap()); 29 | axum::serve(listener, app).await.unwrap(); 30 | } 31 | 32 | async fn handler() -> Html<&'static str> { 33 | Html("

Hello, World!

") 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019–2025 axum Contributors 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: If something isn't working as expected 🤔. 4 | --- 5 | 6 | 11 | 12 | - [ ] I have looked for existing issues (including closed) about this 13 | 14 | ## Bug Report 15 | 16 | ### Version 17 | 18 | 24 | 25 | ### Platform 26 | 27 | 30 | 31 | ### Crates 32 | 33 | 37 | 38 | ### Description 39 | 40 | 55 | -------------------------------------------------------------------------------- /axum-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Core types and traits for [`axum`]. 2 | //! 3 | //! Libraries authors that want to provide [`FromRequest`] or [`IntoResponse`] implementations 4 | //! should depend on the [`axum-core`] crate, instead of `axum` if possible. 5 | //! 6 | //! [`FromRequest`]: crate::extract::FromRequest 7 | //! [`IntoResponse`]: crate::response::IntoResponse 8 | //! [`axum`]: https://crates.io/crates/axum 9 | //! [`axum-core`]: http://crates.io/crates/axum-core 10 | 11 | #![cfg_attr(test, allow(clippy::float_cmp))] 12 | #![cfg_attr(not(test), warn(clippy::print_stdout, clippy::dbg_macro))] 13 | 14 | #[macro_use] 15 | pub(crate) mod macros; 16 | #[doc(hidden)] // macro helpers 17 | pub mod __private { 18 | #[cfg(feature = "tracing")] 19 | pub use tracing; 20 | } 21 | 22 | mod error; 23 | mod ext_traits; 24 | pub use self::error::Error; 25 | 26 | pub mod body; 27 | pub mod extract; 28 | pub mod response; 29 | 30 | /// Alias for a type-erased error type. 31 | pub type BoxError = Box; 32 | 33 | pub use self::ext_traits::{request::RequestExt, request_parts::RequestPartsExt}; 34 | 35 | #[cfg(test)] 36 | use axum_macros::__private_axum_test as test; 37 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/single_wrong_return_tuple.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NotIntoResponse: IntoResponse` is not satisfied 2 | --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 3 | | 4 | 6 | async fn handler() -> (NotIntoResponse) { 5 | | ^^^^^^^^^^^^^^^^^ unsatisfied trait bound 6 | | 7 | help: the trait `IntoResponse` is not implemented for `NotIntoResponse` 8 | --> tests/debug_handler/fail/single_wrong_return_tuple.rs:3:1 9 | | 10 | 3 | struct NotIntoResponse; 11 | | ^^^^^^^^^^^^^^^^^^^^^^ 12 | = help: the following other types implement trait `IntoResponse`: 13 | &'static [u8; N] 14 | &'static [u8] 15 | &'static str 16 | () 17 | (R,) 18 | (Response<()>, R) 19 | (Response<()>, T1, R) 20 | (Response<()>, T1, T2, R) 21 | and $N others 22 | note: required by a bound in `__axum_macros_check_handler_into_response::{closure#0}::check` 23 | --> tests/debug_handler/fail/single_wrong_return_tuple.rs:6:23 24 | | 25 | 6 | async fn handler() -> (NotIntoResponse) { 26 | | ^^^^^^^^^^^^^^^^^ required by this bound in `check` 27 | -------------------------------------------------------------------------------- /axum/src/docs/routing/without_v07_checks.md: -------------------------------------------------------------------------------- 1 | Turn off checks for compatibility with route matching syntax from 0.7. 2 | 3 | This allows usage of paths starting with a colon `:` or an asterisk `*` which are otherwise prohibited. 4 | 5 | # Example 6 | 7 | ```rust 8 | use axum::{ 9 | routing::get, 10 | Router, 11 | }; 12 | 13 | let app = Router::<()>::new() 14 | .without_v07_checks() 15 | .route("/:colon", get(|| async {})) 16 | .route("/*asterisk", get(|| async {})); 17 | 18 | // Our app now accepts 19 | // - GET /:colon 20 | // - GET /*asterisk 21 | # let _: Router = app; 22 | ``` 23 | 24 | Adding such routes without calling this method first will panic. 25 | 26 | ```rust,should_panic 27 | use axum::{ 28 | routing::get, 29 | Router, 30 | }; 31 | 32 | // This panics... 33 | let app = Router::<()>::new() 34 | .route("/:colon", get(|| async {})); 35 | ``` 36 | 37 | # Merging 38 | 39 | When two routers are merged, v0.7 checks are disabled for route registrations on the resulting router if both of the two routers had them also disabled. 40 | 41 | # Nesting 42 | 43 | Each router needs to have the checks explicitly disabled. Nesting a router with the checks either enabled or disabled has no effect on the outer router. 44 | -------------------------------------------------------------------------------- /axum/src/middleware/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for writing middleware 2 | //! 3 | #![doc = include_str!("../docs/middleware.md")] 4 | 5 | mod from_extractor; 6 | mod from_fn; 7 | mod map_request; 8 | mod map_response; 9 | mod response_axum_body; 10 | 11 | pub use self::from_extractor::{ 12 | from_extractor, from_extractor_with_state, FromExtractor, FromExtractorLayer, 13 | }; 14 | pub use self::from_fn::{from_fn, from_fn_with_state, FromFn, FromFnLayer, Next}; 15 | pub use self::map_request::{ 16 | map_request, map_request_with_state, IntoMapRequestResult, MapRequest, MapRequestLayer, 17 | }; 18 | pub use self::map_response::{ 19 | map_response, map_response_with_state, MapResponse, MapResponseLayer, 20 | }; 21 | pub use self::response_axum_body::{ 22 | ResponseAxumBody, ResponseAxumBodyFuture, ResponseAxumBodyLayer, 23 | }; 24 | pub use crate::extension::AddExtension; 25 | 26 | pub mod future { 27 | //! Future types. 28 | 29 | pub use super::from_extractor::ResponseFuture as FromExtractorResponseFuture; 30 | pub use super::from_fn::ResponseFuture as FromFnResponseFuture; 31 | pub use super::map_request::ResponseFuture as MapRequestResponseFuture; 32 | pub use super::map_response::ResponseFuture as MapResponseResponseFuture; 33 | } 34 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/route_layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to the router that will only run if the request matches 2 | a route. 3 | 4 | Note that the middleware is only applied to existing routes. So you have to 5 | first add your routes (and / or fallback) and then call `route_layer` 6 | afterwards. Additional routes added after `route_layer` is called will not have 7 | the middleware added. 8 | 9 | This works similarly to [`MethodRouter::layer`] except the middleware will only run if 10 | the request matches a route. This is useful for middleware that return early 11 | (such as authorization) which might otherwise convert a `405 Method Not Allowed` into a 12 | `401 Unauthorized`. 13 | 14 | # Example 15 | 16 | ```rust 17 | use axum::{ 18 | routing::get, 19 | Router, 20 | }; 21 | use tower_http::validate_request::ValidateRequestHeaderLayer; 22 | 23 | let app = Router::new().route( 24 | "/foo", 25 | get(|| async {}) 26 | .route_layer(ValidateRequestHeaderLayer::bearer("password")) 27 | ); 28 | 29 | // `GET /foo` with a valid token will receive `200 OK` 30 | // `GET /foo` with a invalid token will receive `401 Unauthorized` 31 | // `POST /FOO` with a invalid token will receive `405 Method Not Allowed` 32 | # let _: Router = app; 33 | ``` 34 | -------------------------------------------------------------------------------- /examples/customize-extractor-error/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-customize-extractor-error 5 | //! ``` 6 | 7 | mod custom_extractor; 8 | mod derive_from_request; 9 | mod with_rejection; 10 | 11 | use axum::{routing::post, Router}; 12 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 13 | 14 | #[tokio::main] 15 | async fn main() { 16 | tracing_subscriber::registry() 17 | .with( 18 | tracing_subscriber::EnvFilter::try_from_default_env() 19 | .unwrap_or_else(|_| format!("{}=trace", env!("CARGO_CRATE_NAME")).into()), 20 | ) 21 | .with(tracing_subscriber::fmt::layer()) 22 | .init(); 23 | 24 | // Build our application with some routes 25 | let app = Router::new() 26 | .route("/with-rejection", post(with_rejection::handler)) 27 | .route("/custom-extractor", post(custom_extractor::handler)) 28 | .route("/derive-from-request", post(derive_from_request::handler)); 29 | 30 | // Run our application 31 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 32 | .await 33 | .unwrap(); 34 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 35 | axum::serve(listener, app).await.unwrap(); 36 | } 37 | -------------------------------------------------------------------------------- /examples/routes-and-handlers-close-together/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-routes-and-handlers-close-together 5 | //! ``` 6 | 7 | use axum::{ 8 | routing::{get, post, MethodRouter}, 9 | Router, 10 | }; 11 | 12 | #[tokio::main] 13 | async fn main() { 14 | let app = Router::new() 15 | .merge(root()) 16 | .merge(get_foo()) 17 | .merge(post_foo()); 18 | 19 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 20 | .await 21 | .unwrap(); 22 | println!("listening on {}", listener.local_addr().unwrap()); 23 | axum::serve(listener, app).await.unwrap(); 24 | } 25 | 26 | fn root() -> Router { 27 | async fn handler() -> &'static str { 28 | "Hello, World!" 29 | } 30 | 31 | route("/", get(handler)) 32 | } 33 | 34 | fn get_foo() -> Router { 35 | async fn handler() -> &'static str { 36 | "Hi from `GET /foo`" 37 | } 38 | 39 | route("/foo", get(handler)) 40 | } 41 | 42 | fn post_foo() -> Router { 43 | async fn handler() -> &'static str { 44 | "Hi from `POST /foo`" 45 | } 46 | 47 | route("/foo", post(handler)) 48 | } 49 | 50 | fn route(path: &str, method_router: MethodRouter<()>) -> Router { 51 | Router::new().route(path, method_router) 52 | } 53 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/parts_extracting_body.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `String: FromRequestParts<_>` is not satisfied 2 | --> tests/from_request/fail/parts_extracting_body.rs:5:11 3 | | 4 | 5 | body: String, 5 | | ^^^^^^ the trait `FromRequestParts<_>` is not implemented for `String` 6 | | 7 | = note: Function argument is not a valid axum extractor. 8 | See `https://docs.rs/axum/0.8/axum/extract/index.html` for details 9 | = help: the following other types implement trait `FromRequestParts`: 10 | `()` implements `FromRequestParts` 11 | `(T1, T2)` implements `FromRequestParts` 12 | `(T1, T2, T3)` implements `FromRequestParts` 13 | `(T1, T2, T3, T4)` implements `FromRequestParts` 14 | `(T1, T2, T3, T4, T5)` implements `FromRequestParts` 15 | `(T1, T2, T3, T4, T5, T6)` implements `FromRequestParts` 16 | `(T1, T2, T3, T4, T5, T6, T7)` implements `FromRequestParts` 17 | `(T1, T2, T3, T4, T5, T6, T7, T8)` implements `FromRequestParts` 18 | and $N others 19 | 20 | error[E0282]: type annotations needed 21 | --> tests/from_request/fail/parts_extracting_body.rs:5:11 22 | | 23 | 5 | body: String, 24 | | ^^^^^^ cannot infer type 25 | -------------------------------------------------------------------------------- /examples/compression/src/main.rs: -------------------------------------------------------------------------------- 1 | use axum::{routing::post, Json, Router}; 2 | use serde_json::Value; 3 | use tower::ServiceBuilder; 4 | use tower_http::{compression::CompressionLayer, decompression::RequestDecompressionLayer}; 5 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 6 | 7 | #[cfg(test)] 8 | mod tests; 9 | 10 | #[tokio::main] 11 | async fn main() { 12 | tracing_subscriber::registry() 13 | .with( 14 | tracing_subscriber::EnvFilter::try_from_default_env() 15 | .unwrap_or_else(|_| format!("{}=trace", env!("CARGO_CRATE_NAME")).into()), 16 | ) 17 | .with(tracing_subscriber::fmt::layer()) 18 | .init(); 19 | 20 | let app: Router = app(); 21 | 22 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 23 | .await 24 | .unwrap(); 25 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 26 | axum::serve(listener, app).await.unwrap(); 27 | } 28 | 29 | fn app() -> Router { 30 | Router::new().route("/", post(root)).layer( 31 | ServiceBuilder::new() 32 | .layer(RequestDecompressionLayer::new()) 33 | .layer(CompressionLayer::new()), 34 | ) 35 | } 36 | 37 | async fn root(Json(value): Json) -> Json { 38 | Json(value) 39 | } 40 | -------------------------------------------------------------------------------- /axum-macros/tests/typed_path/pass/customize_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::rejection::PathRejection, 3 | response::{IntoResponse, Response}, 4 | }; 5 | use axum_extra::routing::{RouterExt, TypedPath}; 6 | use serde::Deserialize; 7 | 8 | #[derive(TypedPath, Deserialize)] 9 | #[typed_path("/{foo}", rejection(MyRejection))] 10 | struct MyPathNamed { 11 | foo: String, 12 | } 13 | 14 | #[derive(TypedPath, Deserialize)] 15 | #[typed_path("/", rejection(MyRejection))] 16 | struct MyPathUnit; 17 | 18 | #[derive(TypedPath, Deserialize)] 19 | #[typed_path("/{foo}", rejection(MyRejection))] 20 | struct MyPathUnnamed(String); 21 | 22 | struct MyRejection; 23 | 24 | impl IntoResponse for MyRejection { 25 | fn into_response(self) -> Response { 26 | ().into_response() 27 | } 28 | } 29 | 30 | impl From for MyRejection { 31 | fn from(_: PathRejection) -> Self { 32 | Self 33 | } 34 | } 35 | 36 | impl Default for MyRejection { 37 | fn default() -> Self { 38 | Self 39 | } 40 | } 41 | 42 | fn main() { 43 | _ = axum::Router::<()>::new() 44 | .typed_get(|_: Result| async {}) 45 | .typed_post(|_: Result| async {}) 46 | .typed_put(|_: Result| async {}); 47 | } 48 | -------------------------------------------------------------------------------- /axum/tests/panic_location.rs: -------------------------------------------------------------------------------- 1 | //! Separate test binary, because the panic hook is a global resource 2 | 3 | use std::{ 4 | panic::{catch_unwind, set_hook, take_hook}, 5 | path::Path, 6 | sync::OnceLock, 7 | }; 8 | 9 | use axum::{routing::get, Router}; 10 | 11 | #[test] 12 | fn routes_with_overlapping_method_routes() { 13 | static PANIC_LOCATION_FILE: OnceLock = OnceLock::new(); 14 | 15 | let default_hook = take_hook(); 16 | set_hook(Box::new(|panic_info| { 17 | if let Some(location) = panic_info.location() { 18 | _ = PANIC_LOCATION_FILE.set(location.file().to_owned()); 19 | } 20 | })); 21 | 22 | let result = catch_unwind(|| { 23 | async fn handler() {} 24 | 25 | let _: Router = Router::new() 26 | .route("/foo/bar", get(handler)) 27 | .route("/foo/bar", get(handler)); 28 | }); 29 | set_hook(default_hook); 30 | 31 | let panic_payload = result.unwrap_err(); 32 | let panic_msg = panic_payload.downcast_ref::().unwrap(); 33 | 34 | assert_eq!( 35 | panic_msg, 36 | "Overlapping method route. Handler for `GET /foo/bar` already exists" 37 | ); 38 | 39 | let file = PANIC_LOCATION_FILE.get().unwrap(); 40 | assert_eq!(Path::new(file).file_name().unwrap(), "panic_location.rs"); 41 | } 42 | -------------------------------------------------------------------------------- /axum-macros/src/axum_test.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{parse::Parse, parse_quote, visit_mut::VisitMut, ItemFn}; 4 | 5 | pub(crate) fn expand(_attr: Attrs, mut item_fn: ItemFn) -> TokenStream { 6 | item_fn.attrs.push(parse_quote!(#[tokio::test])); 7 | 8 | let nest_service_fn = replace_nest_with_nest_service(item_fn.clone()); 9 | 10 | quote! { 11 | #item_fn 12 | #nest_service_fn 13 | } 14 | } 15 | 16 | pub(crate) struct Attrs; 17 | 18 | impl Parse for Attrs { 19 | fn parse(_input: syn::parse::ParseStream<'_>) -> syn::Result { 20 | Ok(Self) 21 | } 22 | } 23 | 24 | fn replace_nest_with_nest_service(mut item_fn: ItemFn) -> Option { 25 | item_fn.sig.ident = format_ident!("{}_with_nest_service", item_fn.sig.ident); 26 | 27 | let mut visitor = NestToNestService::default(); 28 | syn::visit_mut::visit_item_fn_mut(&mut visitor, &mut item_fn); 29 | 30 | (visitor.count > 0).then_some(item_fn) 31 | } 32 | 33 | #[derive(Default)] 34 | struct NestToNestService { 35 | count: usize, 36 | } 37 | 38 | impl VisitMut for NestToNestService { 39 | fn visit_expr_method_call_mut(&mut self, i: &mut syn::ExprMethodCall) { 40 | if i.method == "nest" && i.args.len() == 2 { 41 | i.method = parse_quote!(nest_service); 42 | self.count += 1; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/output_tuple_too_many.rs: -------------------------------------------------------------------------------- 1 | use axum::response::AppendHeaders; 2 | 3 | #[axum::debug_handler] 4 | async fn handler() -> ( 5 | axum::http::StatusCode, 6 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 7 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 8 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 9 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 10 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 11 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 12 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 13 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 14 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 15 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 16 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 17 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 18 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 19 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 20 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 21 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 22 | AppendHeaders<[(axum::http::HeaderName, &'static str); 1]>, 23 | axum::http::StatusCode, 24 | ) { 25 | panic!() 26 | } 27 | 28 | fn main() {} 29 | -------------------------------------------------------------------------------- /axum/src/routing/into_make_service.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | convert::Infallible, 3 | future::ready, 4 | task::{Context, Poll}, 5 | }; 6 | use tower_service::Service; 7 | 8 | /// A [`MakeService`] that produces axum router services. 9 | /// 10 | /// [`MakeService`]: tower::make::MakeService 11 | #[derive(Debug, Clone)] 12 | pub struct IntoMakeService { 13 | svc: S, 14 | } 15 | 16 | impl IntoMakeService { 17 | pub(crate) fn new(svc: S) -> Self { 18 | Self { svc } 19 | } 20 | } 21 | 22 | impl Service for IntoMakeService 23 | where 24 | S: Clone, 25 | { 26 | type Response = S; 27 | type Error = Infallible; 28 | type Future = IntoMakeServiceFuture; 29 | 30 | #[inline] 31 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { 32 | Poll::Ready(Ok(())) 33 | } 34 | 35 | fn call(&mut self, _target: T) -> Self::Future { 36 | IntoMakeServiceFuture::new(ready(Ok(self.svc.clone()))) 37 | } 38 | } 39 | 40 | opaque_future! { 41 | /// Response future for [`IntoMakeService`]. 42 | pub type IntoMakeServiceFuture = 43 | std::future::Ready>; 44 | } 45 | 46 | #[cfg(test)] 47 | mod tests { 48 | use super::*; 49 | 50 | #[test] 51 | fn traits() { 52 | use crate::test_helpers::*; 53 | 54 | assert_send::>(); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/tls-rustls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/global-404-handler/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-global-404-handler 5 | //! ``` 6 | 7 | use axum::{ 8 | http::StatusCode, 9 | response::{Html, IntoResponse}, 10 | routing::get, 11 | Router, 12 | }; 13 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | tracing_subscriber::registry() 18 | .with( 19 | tracing_subscriber::EnvFilter::try_from_default_env() 20 | .unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()), 21 | ) 22 | .with(tracing_subscriber::fmt::layer()) 23 | .init(); 24 | 25 | // build our application with a route 26 | let app = Router::new().route("/", get(handler)); 27 | 28 | // add a fallback service for handling routes to unknown paths 29 | let app = app.fallback(handler_404); 30 | 31 | // run it 32 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 33 | .await 34 | .unwrap(); 35 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 36 | axum::serve(listener, app).await.unwrap(); 37 | } 38 | 39 | async fn handler() -> Html<&'static str> { 40 | Html("

Hello, World!

") 41 | } 42 | 43 | async fn handler_404() -> impl IntoResponse { 44 | (StatusCode::NOT_FOUND, "nothing to see here") 45 | } 46 | -------------------------------------------------------------------------------- /examples/low-level-openssl/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/low-level-rustls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/websockets-http2/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/self_signed_certs/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDkzCCAnugAwIBAgIUXVYkRCrM/ge03DVymDtXCuybp7gwDQYJKoZIhvcNAQEL 3 | BQAwWTELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM 4 | GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MB4X 5 | DTIxMDczMTE0MjIxMloXDTIyMDczMTE0MjIxMlowWTELMAkGA1UEBhMCVVMxEzAR 6 | BgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5 7 | IEx0ZDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A 8 | MIIBCgKCAQEA02V5ZjmqLB/VQwTarrz/35qsa83L+DbAoa0001+jVmmC+G9Nufi0 9 | daroFWj/Uicv2fZWETU8JoZKUrX4BK9og5cg5rln/CtBRWCUYIwRgY9R/CdBGPn4 10 | kp+XkSJaCw74ZIyLy/Zfux6h8ES1m9YRnBza+s7U+ImRBRf4MRPtXQ3/mqJxAZYq 11 | dOnKnvssRyD2qutgVTAxwMUvJWIivRhRYDj7WOpS4CEEeQxP1iH1/T5P7FdtTGdT 12 | bVBABCA8JhL96uFGPpOYHcM/7R5EIA3yZ5FNg931QzoDITjtXGtQ6y9/l/IYkWm6 13 | J67RWcN0IoTsZhz0WNU4gAeslVtJLofn8QIDAQABo1MwUTAdBgNVHQ4EFgQUzFnK 14 | NfS4LAYuKeWwHbzooER0yZ0wHwYDVR0jBBgwFoAUzFnKNfS4LAYuKeWwHbzooER0 15 | yZ0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAk4O+e9jia59W 16 | ZwetN4GU7OWcYhmOgSizRSs6u7mTfp62LDMt96WKU3THksOnZ44HnqWQxsSfdFVU 17 | XJD12tjvVU8Z4FWzQajcHeemUYiDze8EAh6TnxnUcOrU8IcwiKGxCWRY/908jnWg 18 | +MMscfMCMYTRdeTPqD8fGzAlUCtmyzH6KLE3s4Oo/r5+NR+Uvrwpdvb7xe0MwwO9 19 | Q/zR4N8ep/HwHVEObcaBofE1ssZLksX7ZgCP9wMgXRWpNAtC5EWxMbxYjBfWFH24 20 | fDJlBMiGJWg8HHcxK7wQhFh+fuyNzE+xEWPsI9VL1zDftd9x8/QsOagyEOnY8Vxr 21 | AopvZ09uEQ== 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /axum-macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | categories = ["asynchronous", "network-programming", "web-programming"] 3 | description = "Macros for axum" 4 | edition = "2021" 5 | rust-version = { workspace = true } 6 | homepage = "https://github.com/tokio-rs/axum" 7 | keywords = ["axum"] 8 | license = "MIT" 9 | name = "axum-macros" 10 | readme = "README.md" 11 | repository = "https://github.com/tokio-rs/axum" 12 | version = "0.5.0" # remember to also bump the version that axum and axum-extra depends on 13 | 14 | [package.metadata.cargo-public-api-crates] 15 | allowed = [] 16 | 17 | [package.metadata.docs.rs] 18 | all-features = true 19 | 20 | [lib] 21 | proc-macro = true 22 | 23 | [features] 24 | default = [] 25 | __private = ["syn/visit-mut"] 26 | 27 | [dependencies] 28 | proc-macro2 = "1.0" 29 | quote = "1.0" 30 | syn = { version = "2.0", features = [ 31 | "full", 32 | "parsing", 33 | # needed for `Hash` impls 34 | "extra-traits", 35 | ] } 36 | 37 | [dev-dependencies] 38 | axum = { path = "../axum", features = ["macros"] } 39 | axum-extra = { path = "../axum-extra", features = ["typed-routing", "cookie-private", "typed-header", "routing"] } 40 | rustversion = "1.0" 41 | serde = { version = "1.0", features = ["derive"] } 42 | serde_json = "1.0" 43 | syn = { version = "2.0", features = ["full", "extra-traits"] } 44 | tokio = { version = "1.25.0", features = ["full"] } 45 | trybuild = "1.0.63" 46 | 47 | [lints] 48 | workspace = true 49 | -------------------------------------------------------------------------------- /axum/src/docs/routing/method_not_allowed_fallback.md: -------------------------------------------------------------------------------- 1 | Add a fallback [`Handler`] for the case where a route exists, but the method of the request is not supported. 2 | 3 | Sets a fallback on all previously registered [`MethodRouter`]s, 4 | to be called when no matching method handler is set. 5 | 6 | ```rust,no_run 7 | use axum::{response::IntoResponse, routing::get, Router}; 8 | 9 | async fn hello_world() -> impl IntoResponse { 10 | "Hello, world!\n" 11 | } 12 | 13 | async fn default_fallback() -> impl IntoResponse { 14 | "Default fallback\n" 15 | } 16 | 17 | async fn handle_405() -> impl IntoResponse { 18 | "Method not allowed fallback" 19 | } 20 | 21 | #[tokio::main] 22 | async fn main() { 23 | let router = Router::new() 24 | .route("/", get(hello_world)) 25 | .fallback(default_fallback) 26 | .method_not_allowed_fallback(handle_405); 27 | 28 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 29 | 30 | axum::serve(listener, router).await.unwrap(); 31 | } 32 | ``` 33 | 34 | The fallback only applies if there is a `MethodRouter` registered for a given path, 35 | but the method used in the request is not specified. In the example, a `GET` on 36 | `http://localhost:3000` causes the `hello_world` handler to react, while issuing a 37 | `POST` triggers `handle_405`. Calling an entirely different route, like `http://localhost:3000/hello` 38 | causes `default_fallback` to run. 39 | -------------------------------------------------------------------------------- /axum/src/docs/routing/route_layer.md: -------------------------------------------------------------------------------- 1 | Apply a [`tower::Layer`] to the router that will only run if the request matches 2 | a route. 3 | 4 | Note that the middleware is only applied to existing routes. So you have to 5 | first add your routes (and / or fallback) and then call `route_layer` 6 | afterwards. Additional routes added after `route_layer` is called will not have 7 | the middleware added. 8 | 9 | This works similarly to [`Router::layer`] except the middleware will only run if 10 | the request matches a route. This is useful for middleware that return early 11 | (such as authorization) which might otherwise convert a `404 Not Found` into a 12 | `401 Unauthorized`. 13 | 14 | This function will panic if no routes have been declared yet on the router, 15 | since the new layer will have no effect, and this is typically a bug. 16 | In generic code, you can test if that is the case first, by calling [`Router::has_routes`]. 17 | 18 | # Example 19 | 20 | ```rust 21 | use axum::{ 22 | routing::get, 23 | Router, 24 | }; 25 | use tower_http::validate_request::ValidateRequestHeaderLayer; 26 | 27 | let app = Router::new() 28 | .route("/foo", get(|| async {})) 29 | .route_layer(ValidateRequestHeaderLayer::bearer("password")); 30 | 31 | // `GET /foo` with a valid token will receive `200 OK` 32 | // `GET /foo` with a invalid token will receive `401 Unauthorized` 33 | // `GET /not-found` with a invalid token will receive `404 Not Found` 34 | # let _: Router = app; 35 | ``` 36 | -------------------------------------------------------------------------------- /axum/src/handler/future.rs: -------------------------------------------------------------------------------- 1 | //! Handler future types. 2 | 3 | use crate::response::Response; 4 | use axum_core::extract::Request; 5 | use futures_util::future::Map; 6 | use pin_project_lite::pin_project; 7 | use std::{convert::Infallible, future::Future, pin::Pin, task::Context}; 8 | use tower::util::Oneshot; 9 | use tower_service::Service; 10 | 11 | opaque_future! { 12 | /// The response future for [`IntoService`](super::IntoService). 13 | pub type IntoServiceFuture = 14 | Map< 15 | F, 16 | fn(Response) -> Result, 17 | >; 18 | } 19 | 20 | pin_project! { 21 | /// The response future for [`Layered`](super::Layered). 22 | pub struct LayeredFuture 23 | where 24 | S: Service, 25 | { 26 | #[pin] 27 | inner: Map, fn(Result) -> Response>, 28 | } 29 | } 30 | 31 | impl LayeredFuture 32 | where 33 | S: Service, 34 | { 35 | pub(super) fn new( 36 | inner: Map, fn(Result) -> Response>, 37 | ) -> Self { 38 | Self { inner } 39 | } 40 | } 41 | 42 | impl Future for LayeredFuture 43 | where 44 | S: Service, 45 | { 46 | type Output = Response; 47 | 48 | #[inline] 49 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> std::task::Poll { 50 | self.project().inner.poll(cx) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /axum-core/src/ext_traits/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod request; 2 | pub(crate) mod request_parts; 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use std::convert::Infallible; 7 | 8 | use crate::extract::{FromRef, FromRequestParts}; 9 | use http::request::Parts; 10 | 11 | #[derive(Debug, Default, Clone, Copy)] 12 | pub(crate) struct State(pub(crate) S); 13 | 14 | impl FromRequestParts for State 15 | where 16 | InnerState: FromRef, 17 | OuterState: Send + Sync, 18 | { 19 | type Rejection = Infallible; 20 | 21 | async fn from_request_parts( 22 | _parts: &mut Parts, 23 | state: &OuterState, 24 | ) -> Result { 25 | let inner_state = InnerState::from_ref(state); 26 | Ok(Self(inner_state)) 27 | } 28 | } 29 | 30 | // some extractor that requires the state, such as `SignedCookieJar` 31 | #[allow(dead_code)] 32 | pub(crate) struct RequiresState(pub(crate) String); 33 | 34 | impl FromRequestParts for RequiresState 35 | where 36 | S: Send + Sync, 37 | String: FromRef, 38 | { 39 | type Rejection = Infallible; 40 | 41 | async fn from_request_parts( 42 | _parts: &mut Parts, 43 | state: &S, 44 | ) -> Result { 45 | Ok(Self(String::from_ref(state))) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{rejection::ExtensionRejection, FromRequest, Request}, 3 | http::StatusCode, 4 | response::{IntoResponse, Response}, 5 | routing::get, 6 | Extension, Router, 7 | }; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequest)] 18 | #[from_request(rejection(MyRejection))] 19 | struct MyExtractor { 20 | one: Extension, 21 | #[from_request(via(Extension))] 22 | two: String, 23 | three: OtherExtractor, 24 | } 25 | 26 | struct OtherExtractor; 27 | 28 | impl FromRequest for OtherExtractor 29 | where 30 | S: Send + Sync, 31 | { 32 | // this rejection doesn't implement `Display` and `Error` 33 | type Rejection = (StatusCode, String); 34 | 35 | async fn from_request(_req: Request, _state: &S) -> Result { 36 | todo!() 37 | } 38 | } 39 | 40 | struct MyRejection {} 41 | 42 | impl From for MyRejection { 43 | fn from(_: ExtensionRejection) -> Self { 44 | todo!() 45 | } 46 | } 47 | 48 | impl From<(StatusCode, String)> for MyRejection { 49 | fn from(_: (StatusCode, String)) -> Self { 50 | todo!() 51 | } 52 | } 53 | 54 | impl IntoResponse for MyRejection { 55 | fn into_response(self) -> Response { 56 | todo!() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/extension_not_clone.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `NonCloneType: Clone` is not satisfied 2 | --> tests/debug_handler/fail/extension_not_clone.rs:7:38 3 | | 4 | 7 | async fn test_extension_non_clone(_: Extension) {} 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `NonCloneType` 6 | | 7 | = help: the following other types implement trait `FromRequest`: 8 | (T1, T2) 9 | (T1, T2, T3) 10 | (T1, T2, T3, T4) 11 | (T1, T2, T3, T4, T5) 12 | (T1, T2, T3, T4, T5, T6) 13 | (T1, T2, T3, T4, T5, T6, T7) 14 | (T1, T2, T3, T4, T5, T6, T7, T8) 15 | (T1, T2, T3, T4, T5, T6, T7, T8, T9) 16 | and $N others 17 | = note: required for `Extension` to implement `FromRequestParts<()>` 18 | = note: required for `Extension` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` 19 | note: required by a bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check` 20 | --> tests/debug_handler/fail/extension_not_clone.rs:7:38 21 | | 22 | 7 | async fn test_extension_non_clone(_: Extension) {} 23 | | ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `__axum_macros_check_test_extension_non_clone_0_from_request_check` 24 | help: consider annotating `NonCloneType` with `#[derive(Clone)]` 25 | | 26 | 4 + #[derive(Clone)] 27 | 5 | struct NonCloneType; 28 | | 29 | -------------------------------------------------------------------------------- /axum/src/test_helpers/counting_cloneable_state.rs: -------------------------------------------------------------------------------- 1 | use std::sync::{ 2 | atomic::{AtomicBool, AtomicUsize, Ordering}, 3 | Arc, 4 | }; 5 | 6 | pub(crate) struct CountingCloneableState { 7 | state: Arc, 8 | } 9 | 10 | struct InnerState { 11 | setup_done: AtomicBool, 12 | count: AtomicUsize, 13 | } 14 | 15 | impl CountingCloneableState { 16 | pub(crate) fn new() -> Self { 17 | let inner_state = InnerState { 18 | setup_done: AtomicBool::new(false), 19 | count: AtomicUsize::new(0), 20 | }; 21 | Self { 22 | state: Arc::new(inner_state), 23 | } 24 | } 25 | 26 | pub(crate) fn setup_done(&self) { 27 | self.state.setup_done.store(true, Ordering::SeqCst); 28 | } 29 | 30 | pub(crate) fn count(&self) -> usize { 31 | self.state.count.load(Ordering::SeqCst) 32 | } 33 | } 34 | 35 | impl Clone for CountingCloneableState { 36 | fn clone(&self) -> Self { 37 | let state = self.state.clone(); 38 | if state.setup_done.load(Ordering::SeqCst) { 39 | let bt = std::backtrace::Backtrace::force_capture(); 40 | let bt = bt 41 | .to_string() 42 | .lines() 43 | .filter(|line| line.contains("axum") || line.contains("./src")) 44 | .collect::>() 45 | .join("\n"); 46 | println!("AppState::Clone:\n===============\n{bt}\n"); 47 | state.count.fetch_add(1, Ordering::SeqCst); 48 | } 49 | 50 | Self { state } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /axum-macros/tests/debug_handler/fail/argument_not_extractor.stderr: -------------------------------------------------------------------------------- 1 | error[E0277]: the trait bound `bool: FromRequest<(), axum_core::extract::private::ViaParts>` is not satisfied 2 | --> tests/debug_handler/fail/argument_not_extractor.rs:4:24 3 | | 4 | 4 | async fn handler(_foo: bool) {} 5 | | ^^^^ the trait `FromRequestParts<()>` is not implemented for `bool` 6 | | 7 | = note: Function argument is not a valid axum extractor. 8 | See `https://docs.rs/axum/0.8/axum/extract/index.html` for details 9 | = help: the following other types implement trait `FromRequestParts`: 10 | `()` implements `FromRequestParts` 11 | `(T1, T2)` implements `FromRequestParts` 12 | `(T1, T2, T3)` implements `FromRequestParts` 13 | `(T1, T2, T3, T4)` implements `FromRequestParts` 14 | `(T1, T2, T3, T4, T5)` implements `FromRequestParts` 15 | `(T1, T2, T3, T4, T5, T6)` implements `FromRequestParts` 16 | `(T1, T2, T3, T4, T5, T6, T7)` implements `FromRequestParts` 17 | `(T1, T2, T3, T4, T5, T6, T7, T8)` implements `FromRequestParts` 18 | and $N others 19 | = note: required for `bool` to implement `FromRequest<(), axum_core::extract::private::ViaParts>` 20 | note: required by a bound in `__axum_macros_check_handler_0_from_request_check` 21 | --> tests/debug_handler/fail/argument_not_extractor.rs:4:24 22 | | 23 | 4 | async fn handler(_foo: bool) {} 24 | | ^^^^ required by this bound in `__axum_macros_check_handler_0_from_request_check` 25 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/pass/override_rejection_parts.rs: -------------------------------------------------------------------------------- 1 | use axum::{ 2 | extract::{rejection::ExtensionRejection, FromRequestParts}, 3 | http::{request::Parts, StatusCode}, 4 | response::{IntoResponse, Response}, 5 | routing::get, 6 | Extension, Router, 7 | }; 8 | 9 | fn main() { 10 | let _: Router = Router::new().route("/", get(handler).post(handler_result)); 11 | } 12 | 13 | async fn handler(_: MyExtractor) {} 14 | 15 | async fn handler_result(_: Result) {} 16 | 17 | #[derive(FromRequestParts)] 18 | #[from_request(rejection(MyRejection))] 19 | struct MyExtractor { 20 | one: Extension, 21 | #[from_request(via(Extension))] 22 | two: String, 23 | three: OtherExtractor, 24 | } 25 | 26 | struct OtherExtractor; 27 | 28 | impl FromRequestParts for OtherExtractor 29 | where 30 | S: Send + Sync, 31 | { 32 | // this rejection doesn't implement `Display` and `Error` 33 | type Rejection = (StatusCode, String); 34 | 35 | async fn from_request_parts(_parts: &mut Parts, _state: &S) -> Result { 36 | todo!() 37 | } 38 | } 39 | 40 | struct MyRejection {} 41 | 42 | impl From for MyRejection { 43 | fn from(_: ExtensionRejection) -> Self { 44 | todo!() 45 | } 46 | } 47 | 48 | impl From<(StatusCode, String)> for MyRejection { 49 | fn from(_: (StatusCode, String)) -> Self { 50 | todo!() 51 | } 52 | } 53 | 54 | impl IntoResponse for MyRejection { 55 | fn into_response(self) -> Response { 56 | todo!() 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)] 2 | --> tests/from_request/fail/generic_without_via.rs:5:18 3 | | 4 | 5 | struct Extractor(T); 5 | | ^ 6 | 7 | error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied 8 | --> tests/from_request/fail/generic_without_via.rs:10:44 9 | | 10 | 10 | _ = Router::<()>::new().route("/", get(foo)); 11 | | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` 12 | | | 13 | | required by a bound introduced by this call 14 | | 15 | = note: Consider using `#[axum::debug_handler]` to improve the error message 16 | = help: the following other types implement trait `Handler`: 17 | `Layered` implements `Handler` 18 | `MethodRouter` implements `Handler<(), S>` 19 | note: required by a bound in `axum::routing::get` 20 | --> $WORKSPACE/axum/src/routing/method_routing.rs 21 | | 22 | | top_level_handler_fn!(get, GET); 23 | | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ 24 | | | | 25 | | | required by a bound in this function 26 | | required by this bound in `get` 27 | = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) 28 | -------------------------------------------------------------------------------- /axum-core/README.md: -------------------------------------------------------------------------------- 1 | # axum-core 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-core/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-core)](https://crates.io/crates/axum-core) 5 | [![Documentation](https://docs.rs/axum-core/badge.svg)](https://docs.rs/axum-core) 6 | 7 | Core types and traits for axum. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-core's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-core 44 | [license]: /axum-core/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-extra/README.md: -------------------------------------------------------------------------------- 1 | # axum-extra 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-extra/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-extra)](https://crates.io/crates/axum-extra) 5 | [![Documentation](https://docs.rs/axum-extra/badge.svg)](https://docs.rs/axum-extra) 6 | 7 | Extra utilities for [`axum`]. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-extra's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-extra 44 | [license]: /axum-extra/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-macros/README.md: -------------------------------------------------------------------------------- 1 | # axum-macros 2 | 3 | [![Build status](https://github.com/tokio-rs/axum/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/tokio-rs/axum-macros/actions/workflows/CI.yml) 4 | [![Crates.io](https://img.shields.io/crates/v/axum-macros)](https://crates.io/crates/axum-macros) 5 | [![Documentation](https://docs.rs/axum-macros/badge.svg)](https://docs.rs/axum-macros) 6 | 7 | Macros for [`axum`]. 8 | 9 | More information about this crate can be found in the [crate documentation][docs]. 10 | 11 | ## Safety 12 | 13 | This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% safe Rust. 14 | 15 | ## Minimum supported Rust version 16 | 17 | axum-macros's MSRV is 1.75. 18 | 19 | ## Getting Help 20 | 21 | You're also welcome to ask in the [Discord channel][chat] or open an [issue] 22 | with your question. 23 | 24 | ## Contributing 25 | 26 | 🎈 Thanks for your help improving the project! We are so happy to have 27 | you! We have a [contributing guide][contributing] to help you get involved in the 28 | `axum` project. 29 | 30 | ## License 31 | 32 | This project is licensed under the [MIT license][license]. 33 | 34 | ### Contribution 35 | 36 | Unless you explicitly state otherwise, any contribution intentionally submitted 37 | for inclusion in `axum` by you, shall be licensed as MIT, without any 38 | additional terms or conditions. 39 | 40 | [`axum`]: https://crates.io/crates/axum 41 | [chat]: https://discord.gg/tokio 42 | [contributing]: /CONTRIBUTING.md 43 | [docs]: https://docs.rs/axum-macros 44 | [license]: /axum-macros/LICENSE 45 | [issue]: https://github.com/tokio-rs/axum/issues/new 46 | -------------------------------------------------------------------------------- /axum-macros/tests/from_request/fail/generic_without_via_rejection.stderr: -------------------------------------------------------------------------------- 1 | error: #[derive(FromRequest)] only supports generics when used with #[from_request(via)] 2 | --> tests/from_request/fail/generic_without_via_rejection.rs:6:18 3 | | 4 | 6 | struct Extractor(T); 5 | | ^ 6 | 7 | error[E0277]: the trait bound `fn(Extractor<()>) -> impl Future {foo}: Handler<_, _>` is not satisfied 8 | --> tests/from_request/fail/generic_without_via_rejection.rs:11:44 9 | | 10 | 11 | _ = Router::<()>::new().route("/", get(foo)); 11 | | --- ^^^ the trait `Handler<_, _>` is not implemented for fn item `fn(Extractor<()>) -> impl Future {foo}` 12 | | | 13 | | required by a bound introduced by this call 14 | | 15 | = note: Consider using `#[axum::debug_handler]` to improve the error message 16 | = help: the following other types implement trait `Handler`: 17 | `Layered` implements `Handler` 18 | `MethodRouter` implements `Handler<(), S>` 19 | note: required by a bound in `axum::routing::get` 20 | --> $WORKSPACE/axum/src/routing/method_routing.rs 21 | | 22 | | top_level_handler_fn!(get, GET); 23 | | ^^^^^^^^^^^^^^^^^^^^^^---^^^^^^ 24 | | | | 25 | | | required by a bound in this function 26 | | required by this bound in `get` 27 | = note: this error originates in the macro `top_level_handler_fn` (in Nightly builds, run with -Z macro-backtrace for more info) 28 | -------------------------------------------------------------------------------- /axum/src/body/mod.rs: -------------------------------------------------------------------------------- 1 | //! HTTP body utilities. 2 | 3 | #[doc(no_inline)] 4 | pub use http_body::Body as HttpBody; 5 | 6 | #[doc(no_inline)] 7 | pub use bytes::Bytes; 8 | 9 | #[doc(inline)] 10 | pub use axum_core::body::{Body, BodyDataStream}; 11 | 12 | use http_body_util::{BodyExt, Limited}; 13 | 14 | /// Converts [`Body`] into [`Bytes`] and limits the maximum size of the body. 15 | /// 16 | /// # Example 17 | /// 18 | /// ```rust 19 | /// use axum::body::{to_bytes, Body}; 20 | /// 21 | /// # async fn foo() -> Result<(), axum_core::Error> { 22 | /// let body = Body::from(vec![1, 2, 3]); 23 | /// // Use `usize::MAX` if you don't care about the maximum size. 24 | /// let bytes = to_bytes(body, usize::MAX).await?; 25 | /// assert_eq!(&bytes[..], &[1, 2, 3]); 26 | /// # Ok(()) 27 | /// # } 28 | /// ``` 29 | /// 30 | /// You can detect if the limit was hit by checking the source of the error: 31 | /// 32 | /// ```rust 33 | /// use axum::body::{to_bytes, Body}; 34 | /// use http_body_util::LengthLimitError; 35 | /// 36 | /// # #[tokio::main] 37 | /// # async fn main() { 38 | /// let body = Body::from(vec![1, 2, 3]); 39 | /// match to_bytes(body, 1).await { 40 | /// Ok(_bytes) => panic!("should have hit the limit"), 41 | /// Err(err) => { 42 | /// let source = std::error::Error::source(&err).unwrap(); 43 | /// assert!(source.is::()); 44 | /// } 45 | /// } 46 | /// # } 47 | /// ``` 48 | pub async fn to_bytes(body: Body, limit: usize) -> Result { 49 | Limited::new(body, limit) 50 | .collect() 51 | .await 52 | .map(|col| col.to_bytes()) 53 | .map_err(axum_core::Error::new) 54 | } 55 | -------------------------------------------------------------------------------- /axum/src/routing/url_params.rs: -------------------------------------------------------------------------------- 1 | use crate::util::PercentDecodedStr; 2 | use http::Extensions; 3 | use matchit::Params; 4 | use std::sync::Arc; 5 | 6 | #[derive(Clone)] 7 | pub(crate) enum UrlParams { 8 | Params(Vec<(Arc, PercentDecodedStr)>), 9 | InvalidUtf8InPathParam { key: Arc }, 10 | } 11 | 12 | pub(super) fn insert_url_params(extensions: &mut Extensions, params: &Params<'_, '_>) { 13 | let current_params = extensions.get_mut(); 14 | 15 | if let Some(UrlParams::InvalidUtf8InPathParam { .. }) = current_params { 16 | // nothing to do here since an error was stored earlier 17 | return; 18 | } 19 | 20 | let params = params 21 | .iter() 22 | .filter(|(key, _)| !key.starts_with(super::NEST_TAIL_PARAM)) 23 | .filter(|(key, _)| !key.starts_with(super::FALLBACK_PARAM)) 24 | .map(|(k, v)| { 25 | if let Some(decoded) = PercentDecodedStr::new(v) { 26 | Ok((Arc::from(k), decoded)) 27 | } else { 28 | Err(Arc::from(k)) 29 | } 30 | }) 31 | .collect::, _>>(); 32 | 33 | match (current_params, params) { 34 | (Some(UrlParams::InvalidUtf8InPathParam { .. }), _) => { 35 | unreachable!("we check for this state earlier in this method") 36 | } 37 | (_, Err(invalid_key)) => { 38 | extensions.insert(UrlParams::InvalidUtf8InPathParam { key: invalid_key }); 39 | } 40 | (Some(UrlParams::Params(current)), Ok(params)) => { 41 | current.extend(params); 42 | } 43 | (None, Ok(params)) => { 44 | extensions.insert(UrlParams::Params(params)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /axum/src/docs/method_routing/fallback.md: -------------------------------------------------------------------------------- 1 | Add a fallback service to the router. 2 | 3 | This service will be called if no routes matches the incoming request. 4 | 5 | ```rust 6 | use axum::{ 7 | Router, 8 | routing::get, 9 | handler::Handler, 10 | response::IntoResponse, 11 | http::{StatusCode, Method, Uri}, 12 | }; 13 | 14 | let handler = get(|| async {}).fallback(fallback); 15 | 16 | let app = Router::new().route("/", handler); 17 | 18 | async fn fallback(method: Method, uri: Uri) -> (StatusCode, String) { 19 | (StatusCode::NOT_FOUND, format!("`{method}` not allowed for {uri}")) 20 | } 21 | # let _: Router = app; 22 | ``` 23 | 24 | ## When used with `MethodRouter::merge` 25 | 26 | Two routers that both have a fallback cannot be merged. Doing so results in a 27 | panic: 28 | 29 | ```rust,should_panic 30 | use axum::{ 31 | routing::{get, post}, 32 | handler::Handler, 33 | response::IntoResponse, 34 | http::{StatusCode, Uri}, 35 | }; 36 | 37 | let one = get(|| async {}).fallback(fallback_one); 38 | 39 | let two = post(|| async {}).fallback(fallback_two); 40 | 41 | let method_route = one.merge(two); 42 | 43 | async fn fallback_one() -> impl IntoResponse { /* ... */ } 44 | async fn fallback_two() -> impl IntoResponse { /* ... */ } 45 | # let app: axum::Router = axum::Router::new().route("/", method_route); 46 | ``` 47 | 48 | ## Setting the `Allow` header 49 | 50 | By default `MethodRouter` will set the `Allow` header when returning `405 Method 51 | Not Allowed`. This is also done when the fallback returns `405 Method Not Allowed` 52 | unless the response generated by the fallback already sets the `Allow` header. 53 | 54 | This means if you use `fallback` to accept additional methods, you should make 55 | sure you set the `Allow` header correctly. 56 | -------------------------------------------------------------------------------- /axum/src/docs/debugging_handler_type_errors.md: -------------------------------------------------------------------------------- 1 | ## Debugging handler type errors 2 | 3 | For a function to be used as a handler it must implement the [`Handler`] trait. 4 | axum provides blanket implementations for functions that: 5 | 6 | - Are `async fn`s. 7 | - Take no more than 16 arguments that all implement `Send`. 8 | - All except the last argument implement [`FromRequestParts`]. 9 | - The last argument implements [`FromRequest`]. 10 | - Returns something that implements [`IntoResponse`]. 11 | - If a closure is used it must implement `Clone + Send` and be 12 | `'static`. 13 | - Returns a future that is `Send`. The most common way to accidentally make a 14 | future `!Send` is to hold a `!Send` type across an await. 15 | 16 | Unfortunately Rust gives poor error messages if you try to use a function 17 | that doesn't quite match what's required by [`Handler`]. 18 | 19 | You might get an error like this: 20 | 21 | ```not_rust 22 | error[E0277]: the trait bound `fn(bool) -> impl Future {handler}: Handler<_, _>` is not satisfied 23 | --> src/main.rs:13:44 24 | | 25 | 13 | let app = Router::new().route("/", get(handler)); 26 | | ^^^^^^^ the trait `Handler<_, _>` is not implemented for `fn(bool) -> impl Future {handler}` 27 | | 28 | ::: axum/src/handler/mod.rs:116:8 29 | | 30 | 116 | H: Handler, 31 | | ------------- required by this bound in `axum::routing::get` 32 | ``` 33 | 34 | This error doesn't tell you _why_ your function doesn't implement 35 | [`Handler`]. It's possible to improve the error with the [`debug_handler`] 36 | proc-macro from the [axum-macros] crate. 37 | 38 | [axum-macros]: https://docs.rs/axum-macros 39 | [`debug_handler`]: https://docs.rs/axum-macros/latest/axum_macros/attr.debug_handler.html 40 | -------------------------------------------------------------------------------- /examples/tls-rustls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/low-level-openssl/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/low-level-rustls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/websockets-http2/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/low-level-native-tls/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/tls-graceful-shutdown/self_signed_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDTZXlmOaosH9VD 3 | BNquvP/fmqxrzcv4NsChrTTTX6NWaYL4b025+LR1qugVaP9SJy/Z9lYRNTwmhkpS 4 | tfgEr2iDlyDmuWf8K0FFYJRgjBGBj1H8J0EY+fiSn5eRIloLDvhkjIvL9l+7HqHw 5 | RLWb1hGcHNr6ztT4iZEFF/gxE+1dDf+aonEBlip06cqe+yxHIPaq62BVMDHAxS8l 6 | YiK9GFFgOPtY6lLgIQR5DE/WIfX9Pk/sV21MZ1NtUEAEIDwmEv3q4UY+k5gdwz/t 7 | HkQgDfJnkU2D3fVDOgMhOO1ca1DrL3+X8hiRabonrtFZw3QihOxmHPRY1TiAB6yV 8 | W0kuh+fxAgMBAAECggEADltu8k1qTFLhJgsXWxTFAAe+PBgfCT2WuaRM2So+qqjB 9 | 12Of0MieYPt5hbK63HaC3nfHgqWt7yPhulpXfOH45C8IcgMXl93MMg0MJr58leMI 10 | +2ojFrIrerHSFm5R1TxwDEwrVm/mMowzDWFtQCc6zPJ8wNn5RuP48HKfTZ3/2fjw 11 | zEjSwPO2wFMfo1EJNTjlI303lFbdFBs67NaX6puh30M7Tn+gznHKyO5a7F57wkIt 12 | fkgnEy/sgMedQlwX7bRpUoD6f0fZzV8Qz4cHFywtYErczZJh3VGitJoO/VCIDdty 13 | RPXOAqVDd7EpP1UUehZlKVWZ0OZMEfRgKbRCel5abQKBgQDwgwrIQ5+BiZv6a0VT 14 | ETeXB+hRbvBinRykNo/RvLc3j1enRh9/zO/ShadZIXgOAiM1Jnr5Gp8KkNGca6K1 15 | myhtad7xYPODYzNXXp6T1OPgZxHZLIYzVUj6ypXeV64Te5ZiDaJ1D49czsq+PqsQ 16 | XRcgBJSNpFtDFiXWpjXWfx8PxwKBgQDhAnLY5Sl2eeQo+ud0MvjwftB/mN2qCzJY 17 | 5AlQpRI4ThWxJgGPuHTR29zVa5iWNYuA5LWrC1y/wx+t5HKUwq+5kxvs+npYpDJD 18 | ZX/w0Glc6s0Jc/mFySkbw9B2LePedL7lRF5OiAyC6D106Sc9V2jlL4IflmOzt4CD 19 | ZTNbLtC6hwKBgHfIzBXxl/9sCcMuqdg1Ovp9dbcZCaATn7ApfHd5BccmHQGyav27 20 | k7XF2xMJGEHhzqcqAxUNrSgV+E9vTBomrHvRvrd5Ec7eGTPqbBA0d0nMC5eeFTh7 21 | wV0miH20LX6Gjt9G6yJiHYSbeV5G1+vOcTYBEft5X/qJjU7aePXbWh0BAoGBAJlV 22 | 5tgCCuhvFloK6fHYzqZtdT6O+PfpW20SMXrgkvMF22h2YvgDFrDwqKRUB47NfHzg 23 | 3yBpxNH1ccA5/w97QO8w3gX3h6qicpJVOAPusu6cIBACFZfjRv1hyszOZwvw+Soa 24 | Fj5kHkqTY1YpkREPYS9V2dIW1Wjic1SXgZDw7VM/AoGAP/cZ3ZHTSCDTFlItqy5C 25 | rIy2AiY0WJsx+K0qcvtosPOOwtnGjWHb1gdaVdfX/IRkSsX4PAOdnsyidNC5/l/m 26 | y8oa+5WEeGFclWFhr4dnTA766o8HrM2UjIgWWYBF2VKdptGnHxFeJWFUmeQC/xeW 27 | w37pCS7ykL+7gp7V0WShYsw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /examples/readme/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Run with 2 | //! 3 | //! ```not_rust 4 | //! cargo run -p example-readme 5 | //! ``` 6 | 7 | use axum::{ 8 | http::StatusCode, 9 | response::IntoResponse, 10 | routing::{get, post}, 11 | Json, Router, 12 | }; 13 | use serde::{Deserialize, Serialize}; 14 | 15 | #[tokio::main] 16 | async fn main() { 17 | // initialize tracing 18 | tracing_subscriber::fmt::init(); 19 | 20 | // build our application with a route 21 | let app = Router::new() 22 | // `GET /` goes to `root` 23 | .route("/", get(root)) 24 | // `POST /users` goes to `create_user` 25 | .route("/users", post(create_user)); 26 | 27 | // run our app with hyper 28 | let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") 29 | .await 30 | .unwrap(); 31 | tracing::debug!("listening on {}", listener.local_addr().unwrap()); 32 | axum::serve(listener, app).await.unwrap(); 33 | } 34 | 35 | // basic handler that responds with a static string 36 | async fn root() -> &'static str { 37 | "Hello, World!" 38 | } 39 | 40 | async fn create_user( 41 | // this argument tells axum to parse the request body 42 | // as JSON into a `CreateUser` type 43 | Json(payload): Json, 44 | ) -> impl IntoResponse { 45 | // insert your application logic here 46 | let user = User { 47 | id: 1337, 48 | username: payload.username, 49 | }; 50 | 51 | // this will be converted into a JSON response 52 | // with a status code of `201 Created` 53 | (StatusCode::CREATED, Json(user)) 54 | } 55 | 56 | // the input to our `create_user` handler 57 | #[derive(Deserialize)] 58 | struct CreateUser { 59 | username: String, 60 | } 61 | 62 | // the output to our `create_user` handler 63 | #[derive(Serialize)] 64 | struct User { 65 | id: u64, 66 | username: String, 67 | } 68 | -------------------------------------------------------------------------------- /axum-extra/src/response/error_response.rs: -------------------------------------------------------------------------------- 1 | use axum_core::response::{IntoResponse, Response}; 2 | use http::StatusCode; 3 | use std::error::Error; 4 | use tracing::error; 5 | 6 | /// Convenience response to create an error response from a non-[`IntoResponse`] error 7 | /// 8 | /// This provides a method to quickly respond with an error that does not implement 9 | /// the `IntoResponse` trait itself. This type should only be used for debugging purposes or internal 10 | /// facing applications, as it includes the full error chain with descriptions, 11 | /// thus leaking information that could possibly be sensitive. 12 | /// 13 | /// ```rust 14 | /// use axum_extra::response::InternalServerError; 15 | /// use axum_core::response::IntoResponse; 16 | /// # use std::io::{Error, ErrorKind}; 17 | /// # fn try_thing() -> Result<(), Error> { 18 | /// # Err(Error::new(ErrorKind::Other, "error")) 19 | /// # } 20 | /// 21 | /// async fn maybe_error() -> Result> { 22 | /// try_thing().map_err(InternalServerError)?; 23 | /// // do something on success 24 | /// # Ok(String::from("ok")) 25 | /// } 26 | /// ``` 27 | #[derive(Debug)] 28 | pub struct InternalServerError(pub T); 29 | 30 | impl IntoResponse for InternalServerError { 31 | fn into_response(self) -> Response { 32 | error!(error = &self.0 as &dyn Error); 33 | ( 34 | StatusCode::INTERNAL_SERVER_ERROR, 35 | "An error occurred while processing your request.", 36 | ) 37 | .into_response() 38 | } 39 | } 40 | 41 | #[cfg(test)] 42 | mod tests { 43 | use super::*; 44 | use std::io::Error; 45 | 46 | #[test] 47 | fn internal_server_error() { 48 | let response = InternalServerError(Error::other("Test")).into_response(); 49 | assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /axum-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | categories = ["asynchronous", "network-programming", "web-programming"] 3 | description = "Core types and traits for axum" 4 | edition = "2021" 5 | rust-version = { workspace = true } 6 | homepage = "https://github.com/tokio-rs/axum" 7 | keywords = ["http", "web", "framework"] 8 | license = "MIT" 9 | name = "axum-core" 10 | readme = "README.md" 11 | repository = "https://github.com/tokio-rs/axum" 12 | version = "0.5.5" # remember to bump the version that axum and axum-extra depend on 13 | 14 | [package.metadata.cargo-public-api-crates] 15 | allowed = [ 16 | # not 1.0 17 | "futures_core", 18 | "tower_layer", 19 | # >=1.0 20 | "bytes", 21 | "http", 22 | "http_body", 23 | ] 24 | 25 | [package.metadata.cargo-machete] 26 | ignored = ["tower-http"] # See __private_docs feature 27 | 28 | [package.metadata.docs.rs] 29 | all-features = true 30 | 31 | [features] 32 | tracing = ["dep:tracing"] 33 | 34 | # Required for intra-doc links to resolve correctly 35 | __private_docs = ["dep:tower-http"] 36 | 37 | [dependencies] 38 | bytes = "1.2" 39 | futures-core = "0.3" 40 | http = "1.0.0" 41 | http-body = "1.0.0" 42 | http-body-util = "0.1.0" 43 | mime = "0.3.16" 44 | pin-project-lite = "0.2.7" 45 | sync_wrapper = "1.0.0" 46 | tower-layer = "0.3" 47 | tower-service = "0.3" 48 | 49 | # optional dependencies 50 | tower-http = { version = "0.6.0", optional = true, features = ["limit"] } 51 | tracing = { version = "0.1.37", default-features = false, optional = true } 52 | 53 | [dev-dependencies] 54 | axum = { path = "../axum", features = ["__private"] } 55 | axum-extra = { path = "../axum-extra", features = ["typed-header"] } 56 | axum-macros = { path = "../axum-macros", features = ["__private"] } 57 | hyper = "1.0.0" 58 | tokio = { version = "1.25.0", features = ["macros"] } 59 | tower-http = { version = "0.6.0", features = ["limit"] } 60 | 61 | [lints] 62 | workspace = true 63 | -------------------------------------------------------------------------------- /axum/src/docs/routing/fallback.md: -------------------------------------------------------------------------------- 1 | Add a fallback [`Handler`] to the router. 2 | 3 | This service will be called if no routes matches the incoming request. 4 | 5 | ```rust 6 | use axum::{ 7 | Router, 8 | routing::get, 9 | handler::Handler, 10 | response::IntoResponse, 11 | http::{StatusCode, Uri}, 12 | }; 13 | 14 | let app = Router::new() 15 | .route("/foo", get(|| async { /* ... */ })) 16 | .fallback(fallback); 17 | 18 | async fn fallback(uri: Uri) -> (StatusCode, String) { 19 | (StatusCode::NOT_FOUND, format!("No route for {uri}")) 20 | } 21 | # let _: Router = app; 22 | ``` 23 | 24 | Fallbacks only apply to routes that aren't matched by anything in the 25 | router. If a handler is matched by a request but returns 404 the 26 | fallback is not called. Note that this applies to [`MethodRouter`]s too: if the 27 | request hits a valid path but the [`MethodRouter`] does not have an appropriate 28 | method handler installed, the fallback is not called (use 29 | [`MethodRouter::fallback`] for this purpose instead). 30 | 31 | 32 | # Handling all requests without other routes 33 | 34 | Using `Router::new().fallback(...)` to accept all request regardless of path or 35 | method, if you don't have other routes, isn't optimal: 36 | 37 | ```rust 38 | use axum::Router; 39 | 40 | async fn handler() {} 41 | 42 | let app = Router::new().fallback(handler); 43 | 44 | # async { 45 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 46 | axum::serve(listener, app).await.unwrap(); 47 | # }; 48 | ``` 49 | 50 | Running the handler directly is faster since it avoids the overhead of routing: 51 | 52 | ```rust 53 | use axum::handler::HandlerWithoutStateExt; 54 | 55 | async fn handler() {} 56 | 57 | # async { 58 | let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap(); 59 | axum::serve(listener, handler.into_make_service()).await.unwrap(); 60 | # }; 61 | ``` 62 | --------------------------------------------------------------------------------