4 releases (stable)

2.0.1 May 7, 2024
2.0.0 Nov 27, 2023
1.0.0 Apr 26, 2023
0.2.0 Apr 21, 2023

#1047 in HTTP server

Download history 36/week @ 2024-07-28 22/week @ 2024-09-29

234 downloads per month

MIT license

165KB
3K SLoC

Springtime Web Axum

crates.io version build status

Web framework based on Springtime application framework and axum. Inspired by the Spring Framework in Java, Springtime Web Axum provides a way to create advanced modular Rust web applications by ensuring all components of the application are properly decoupled from each other, and are managed by the dependency injection system.

While axum provides a way to explicitly create web handlers in an imperative way, this crate gives the option to create multi-layer applications in a declarative way, leveraging underlying dependency injection. This enables rapid application development and loose coupling between components.

Features

  • Automatic controller discovery (web handlers with dependency injection)
  • Hierarchical router paths
  • Multiple server instances support with controller filtering
  • Built-in external file and programmable configuration
  • Advanced router configuration
  • All the features provided by axum

Basic usage

Springtime Web Axum is highly configurable, but the most basic usage example is quite simple and consists of declaring controllers, creating an Application instance and calling run(). For tutorial, advanced features, and patterns, please look at the examples, which form a step-by-step guide.

The following example assumes familiarity with springtime and springtime-di.

use axum::extract::Path;
use springtime::application;
use springtime_di::instance_provider::ComponentInstancePtr;
use springtime_di::{injectable, Component, component_alias};
use springtime_web_axum::controller;

// injectable example trait representing a domain service
#[injectable]
trait DomainService {
    fn get_important_message(&self, user: &str) -> String;
}

// concrete service implementation
#[derive(Component)]
struct ExampleDomainService;

// register ExampleDomainService as providing dyn DomainService
#[component_alias]
impl DomainService for ExampleDomainService {
    fn get_important_message(&self, user: &str) -> String {
        format!("Hello {}!", user)
    }
}

// create a struct which will serve as our Controller - this implies it needs to be a Component for
// the dependency injection to work
#[derive(Component)]
struct ExampleController {
    // inject the domain service (alternatively, inject concrete type instead of a trait)
    service: ComponentInstancePtr<dyn DomainService + Send + Sync>,
}

// mark the struct as a Controller - this will scan all functions for the controller attributes and
// create axum handlers out of them
#[controller]
impl ExampleController {
    // this function will respond to GET request for http://localhost/ (or any network interface)
    #[get("/")]
    async fn hello_world(&self) -> &'static str {
        "Hello world!"
    }

    // all axum features are available for controllers
    #[get("/:user")]
    async fn hello_user(&self, Path(user): Path<String>) -> String {
        // delegate work to our domain service
        self.service.get_important_message(&user)
    }
}

// note: for the sake of simplicity, errors are unwrapped, rather than gracefully handled
#[tokio::main]
async fn main() {
    let mut application =
        application::create_default().expect("unable to create application");

    // run our server with default configuration - requests should be forwarded to ExampleController
    application.run().await.expect("error running application");
}

Dependencies

~11–19MB
~260K SLoC