Ergonomic and modular web framework built with Tokio, Tower, and Hyper https://crates.io/crates/axum
Find a file
David Pedersen 7eb2c40b24
Add Router::route_layer (#474)
This addresses something thats been bothering me for some time: Most middleware need to run regardless if the request matches a route or not. For example you don't wanna skip logging for unmatched requests.

However middleware such as authorization only make sense to run for matching requests. This previously wasn't possible to express and you'd have to manually apply the middleware to each handler. Consider this:

```rust
Router::new()
    .route("/foo", get(|| async {}))
    .layer(RequireAuthorizationLayer::bearer("password"));
```

Calling `GET /foo` with an invalid token would receive `401 Unauthorized` as expected however calling some unknown route like `GET /not-found` would also return `401 Unauthorized`. I think this is unexpected and have seen a few users ask questions about it.

It happened because the 404 you'd otherwise see is generated by a fallback service stored on `Router`. When adding a layer to the router the layer would also be applied to the fallback, which in the case of auth means the fallback would never be called for unauthorized requests.

I think what axum does today is the right default however I still think we should support this somehow. Especially since [`extractor_middleware`](https://docs.rs/axum/0.3.1/axum/extract/fn.extractor_middleware.html) is mainly useful for auth but it doesn't work great today due to this gotcha.

This PR proposes adding `Router::layer_on_matching_route` which only applies layers to routes, not the fallback, which fixes the issue. I'm not a big fan of the name `layer_on_matching_route`, would like something shorter, but I think it communicates the purpose decently.

The generics are a bit different since the request body used on the routes and the fallback must match, so layers that changes the request body type are not compatible with `layer_on_matching_route`. Such middleware are very rare so that should be fine.
2021-11-08 18:48:02 +01:00
.github Move axum crate into workspace subfolder (#458) 2021-11-03 12:38:48 +01:00
axum Add Router::route_layer (#474) 2021-11-08 18:48:02 +01:00
examples Update tracing-subscriber to 0.3 (#472) 2021-11-06 14:50:23 +00:00
.clippy.toml Add Headers response (#193) 2021-08-17 17:28:02 +02:00
.gitignore Initial pile of hacks 2021-05-29 21:13:06 +02:00
Cargo.toml Move axum crate into workspace subfolder (#458) 2021-11-03 12:38:48 +01:00
CHANGELOG.md Move axum crate into workspace subfolder (#458) 2021-11-03 12:38:48 +01:00
CONTRIBUTING.md Contributing guide fixes 2021-08-03 21:44:49 +02:00
deny.toml Add async-graphql example (#93) 2021-08-04 12:10:20 +02:00
ECOSYSTEM.md Add axum-flash to list of community maintained crates 2021-11-07 17:17:13 +01:00
README.md Move axum crate into workspace subfolder (#458) 2021-11-03 12:38:48 +01:00

axum

axum is a web application framework that focuses on ergonomics and modularity.

Build status Crates.io Documentation

More information about this crate can be found in the crate documentation.

High level features

  • Route requests to handlers with a macro free API.
  • Declaratively parse requests using extractors.
  • Simple and predictable error handling model.
  • Generate responses with minimal boilerplate.
  • Take full advantage of the tower and tower-http ecosystem of middleware, services, and utilities.

In particular the last point is what sets axum apart from other frameworks. axum doesn't have its own middleware system but instead uses tower::Service. This means axum gets timeouts, tracing, compression, authorization, and more, for free. It also enables you to share middleware with applications written using hyper or tonic.

Usage example

use axum::{
    routing::{get, post},
    http::StatusCode,
    response::IntoResponse,
    Json, Router,
};
use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[tokio::main]
async fn main() {
    // initialize tracing
    tracing_subscriber::fmt::init();

    // build our application with a route
    let app = Router::new()
        // `GET /` goes to `root`
        .route("/", get(root))
        // `POST /users` goes to `create_user`
        .route("/users", post(create_user));

    // run our app with hyper
    // `axum::Server` is a re-export of `hyper::Server`
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    tracing::debug!("listening on {}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

// basic handler that responds with a static string
async fn root() -> &'static str {
    "Hello, World!"
}

async fn create_user(
    // this argument tells axum to parse the request body
    // as JSON into a `CreateUser` type
    Json(payload): Json<CreateUser>,
) -> impl IntoResponse {
    // insert your application logic here
    let user = User {
        id: 1337,
        username: payload.username,
    };

    // this will be converted into a JSON response
    // with a status code of `201 Created`
    (StatusCode::CREATED, Json(user))
}

// the input to our `create_user` handler
#[derive(Deserialize)]
struct CreateUser {
    username: String,
}

// the output to our `create_user` handler
#[derive(Serialize)]
struct User {
    id: u64,
    username: String,
}

You can find this example as well as other example projects in the example directory.

See the crate documentation for way more examples.

Performance

axum is a relatively thin layer on top of hyper and adds very little overhead. So axum's performance is comparable to hyper. You can find a benchmark here.

Safety

This crate uses #![forbid(unsafe_code)] to ensure everything is implemented in 100% safe Rust.

Minimum supported Rust version

axum's MSRV is 1.54.

Examples

The examples folder contains various examples of how to use axum. The docs also have lots of examples

Getting Help

In the axum's repo we also have a number of examples showing how to put everything together. You're also welcome to ask in the Discord channel or open an issue with your question.

Community projects

See here for a list of community maintained crates and projects built with axum.

Contributing

🎈 Thanks for your help improving the project! We are so happy to have you! We have a contributing guide to help you get involved in the axum project.

License

This project is licensed under the MIT license.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in axum by you, shall be licensed as MIT, without any additional terms or conditions.