spring-web 0.2.1

Integration of rust application framework spring-rs and Axum web framework
Documentation
[![crates.io](https://img.shields.io/crates/v/spring-web.svg)](https://crates.io/crates/spring-web)
[![Documentation](https://docs.rs/spring-web/badge.svg)](https://docs.rs/spring-web)

[Axum](https://github.com/tokio-rs/axum) is one of the best web frameworks in the Rust community. It is a sub-project based on [hyper](https://github.com/hyperium/hyper) maintained by Tokio. Axum provides web routing, declarative HTTP request parsing, HTTP response serialization, and can be combined with the middleware in the [tower](https://github.com/tower-rs) ecosystem.

## Dependencies

```toml
spring-web = { version = "0.1.1" }
```

## Configuration items

```toml
[web]
binding = "172.20.10.4"  # IP address of the network card to bind, default 0.0.0.0
port = 8000              # Port number to bind, default 8080
connect_info = false     # Whether to use client connection information, default false
graceful = true          # Whether to enable graceful shutdown, default false

# Web middleware configuration
[web.middlewares]
compression = { enable = true }                        # Enable compression middleware
logger = { enable = true }                             # Enable log middleware
catch_panic = { enable = true }                        # Capture panic generated by handler
limit_payload = { enable = true, body_limit = "5MB" }  # Limit request body size
timeout_request = { enable = true, timeout = 60000 }   # Request timeout 60s

# Cross-domain configuration
cors = { enable = true, allow_origins = [
    "*.github.io",
], allow_headers = [
    "Authentication",
], allow_methods = [
    "GET",
    "POST",
], max_age = 60 }

# Static resource configuration
static = { enable = true, uri = "/static", path = "static", precompressed = true, fallback = "index.html" }
```

## API interface

App implements the [WebConfigurator](https://docs.rs/spring-web/latest/spring_web/trait.WebConfigurator.html) feature, which can be used to specify routing configuration:

```no_run, rust, linenos, hl_lines=6 10-18
use spring::App;
use spring_web::get;
use spring_web::{WebPlugin, WebConfigurator, Router, axum::response::IntoResponse, handler::TypeRouter};
use spring_sqlx::SqlxPlugin;

#[tokio::main]
async fn main() {
    App::new()
      .add_plugin(SqlxPlugin)
      .add_plugin(WebPlugin)
      .add_router(router())
      .run()
      .await
}

fn router() -> Router {
    Router::new().typed_route(hello_word)
}

#[get("/")]
async fn hello_word() -> impl IntoResponse {
   "hello word"
}
```

You can also use the `auto_config` macro to implement automatic configuration. This process macro will automatically register the routes marked by the Procedural Macro into the app:

```diff
+#[auto_config(WebConfigurator)]
 #[tokio::main]
 async fn main() {
    App::new()
    .add_plugin(SqlxPlugin)
    .add_plugin(WebPlugin)
-   .add_router(router())
    .run()
    .await
}

-fn router() -> Router {
-    Router::new().typed_route(hello_word)
-}
```

## Attribute macro

[`get`](https://docs.rs/spring-macros/latest/spring_macros/attr.get.html) in the above example is an attribute macro. `spring-web` provides eight standard HTTP METHOD process macros: `get`, `post`, `patch`, `put`, `delete`, `head`, `trace`, `options`.

You can also use the [`route`](https://docs.rs/spring-macros/latest/spring_macros/attr.route.html) macro to bind multiple methods at the same time:

```rust
use spring_web::route;
use spring_web::axum::response::IntoResponse;

#[route("/test", method = "GET", method = "HEAD")]
async fn example() -> impl IntoResponse {
    "hello world"
}
```

In addition, spring also supports binding multiple routes to a handler, which requires the [`routes`](https://docs.rs/spring-macros/latest/spring_macros/attr.routes.html) attribute macro:

```rust
use spring_web::{routes, get, delete};
use spring_web::axum::response::IntoResponse;

#[routes]
#[get("/test")]
#[get("/test2")]
#[delete("/test")]
async fn example() -> impl IntoResponse {
    "hello world"
}
```

## Extract the Component registered by the plugin

In the above example, the `SqlxPlugin` plugin automatically registers a Sqlx connection pool component for us. We can use `Component` to extract this connection pool from State. [`Component`](https://docs.rs/spring-web/latest/spring_web/extractor/struct.Component.html) is an axum [extractor](https://docs.rs/axum/latest/axum/extract/index.html).

```rust
use anyhow::Context;
use spring_web::get;
use spring_web::{axum::response::IntoResponse, extractor::Component, error::Result};
use spring_sqlx::{ConnectPool, sqlx::{self, Row}};

#[get("/version")]
async fn mysql_version(Component(pool): Component<ConnectPool>) -> Result<String> {
    let version = sqlx::query("select version() as version")
        .fetch_one(&pool)
        .await
        .context("sqlx query failed")?
        .get("version");
    Ok(version)
}
```

Axum also provides other [extractors](https://docs.rs/axum/latest/axum/extract/index.html), which are reexported under [`spring_web::extractor`](https://docs.rs/spring-web/latest/spring_web/extractor/index.html).

## Read configuration

You can use [`Config`](https://docs.rs/spring-web/latest/spring_web/extractor/struct.Config.html) to extract the configuration in the toml file.


```rust
use spring_web::get;
use spring_web::{extractor::Config, axum::response::IntoResponse};
use spring::config::Configurable;
use serde::Deserialize;

#[derive(Debug, Configurable, Deserialize)]
#[config_prefix = "custom"]
struct CustomConfig {
    a: u32,
    b: bool,
}

#[get("/config")]
async fn use_toml_config(Config(conf): Config<CustomConfig>) -> impl IntoResponse {
    format!("a={}, b={}", conf.a, conf.b)
}
```

Add the corresponding configuration to your configuration file:

```toml
[custom]
a = 1
b = true
```

Complete code reference [`web-example`][web-example]

[web-example]: https://github.com/spring-rs/spring-rs/tree/master/examples/web-example

## Use Extractor in Middleware

You can also use [Extractor in middleware](https://docs.rs/axum/latest/axum/middleware/fn.from_fn.html), but please note that you need to follow the rules of axum.

```rust
use spring_web::{axum::{response::Response, middleware::Next}, extractor::{Request, Component}};
use spring_sqlx::ConnectPool;

async fn problem_middleware(Component(db): Component<ConnectPool>, request: Request, next: Next) -> Response {
    // do something
    let response = next.run(request).await;

    response
}
```

Complete code reference [`web-middleware-example`][web-middleware-example]

[web-middleware-example]: https://github.com/spring-rs/spring-rs/tree/master/examples/web-middleware-example