<div align="center">
# rust-webx
**An ASP.NET Core-inspired Web API framework for Rust, built on DI + Mediator.**
A type-safe, convention-over-configuration platform where *requests are endpoints* — declare a route with one attribute, implement a handler, and the framework takes care of routing, dependency injection, middleware, and error mapping.
[](https://crates.io/crates/rust-webx)
[](LICENSE)
[](https://github.com/Rust-Framework/rust-webx/actions/workflows/ci.yml)

</div>
---
## Table of Contents
- [What is rust-webx](#what-is-rust-webx)
- [Key Features](#key-features)
- [Architecture](#architecture)
- [Request-as-Endpoint: Hello World](#request-as-endpoint-hello-world)
- [Core Concepts](#core-concepts)
- [Example Applications](#example-applications)
- [Quick Start](#quick-start)
- [Production Readiness](#production-readiness)
- [Environment Variables](#environment-variables)
- [Error Mapping](#error-mapping)
- [Documentation](#documentation)
- [License](#license)
---
## What is rust-webx
**rust-webx** is a Web API framework for Rust inspired by **ASP.NET Core**. It uses **DI (Dependency Injection) + Mediator** as its dual core. Through the *request-as-endpoint* pattern, developers define complete HTTP APIs with type-safe Rust code — no hand-written route tables, no manual handler registration, no scattered error handling.
Instead of assembling routing libraries, DI containers, middleware and error handling yourself:
```
choose a router → hand-write route tables → design your own DI → wire middleware → unify errors → repeat
```
rust-webx folds these cross-cutting concerns into the framework layer:
| Routing divorced from handlers | `IRequest<T>` carries route metadata; `#[get("/path")]` registers at compile time |
| Handler registration boilerplate | `#[handler]` auto-registers the handler with the DI container |
| Tight coupling between modules | `IMediator::send()` dispatches requests; `publish()` publishes events |
| Scattered error → HTTP mapping | Unified `Error` type + built-in exception middleware |
| Ad-hoc auth/authorization | `add_authentication()` + declarative `#[authorize]` |
rust-webx is **not** a full-stack UI framework (pair it with any frontend), **not** an ORM (bring your own or use [`rust-ef`](https://crates.io/crates/rust-ef)), and **not** a microservice-governance platform.
## Key Features
- **DI + Mediator dual core** — `IRequest<T>` + `IRequestHandler<T, R>` as the carrier; the framework handles route mapping and DI resolution automatically.
- **Compile-time route shortcuts** — `#[get("/path")]`, `#[post("/path")]`, `#[put("/path")]`, `#[delete("/path")]` define a full endpoint in one line.
- **Compile-time scanning** — route metadata is collected at compile time via `inventory`; registered automatically in `Host::build()`.
- **Zero-config handler registration** — the `#[handler]` attribute macro registers a handler with the DI container automatically.
- **Authentication & authorization** — JWT Bearer auth + declarative, grouped route authorization (`#[authorize]`).
- **Identity on the request** — `#[fromclaim] user: IdentityUser` binds the authenticated identity (id, name, email, tenant, roles) as a plain request field; handlers stop parsing tokens. `#[fromclaim(optional)] viewer: Option<IdentityUser>` covers anonymous endpoints (anonymous access must be declared explicitly).
- **Endpoint metadata service** — `IEndpointCatalog` exposes every endpoint at runtime (`all()` / `localized()`), so permission sync, admin navigation and OpenAPI share one source.
- **Localization** — `#[display]` / `#[description]` / `#[category]` store resource keys; a host-wide `Culture.Default` selects the language, with `.json` resources or `include_str!` embedding.
- **Production capabilities out of the box** — graceful shutdown, live health checks, security headers, request IDs, CORS, TLS, rate limiting, compression, OpenAPI + SPA hosting.
- **File upload & download** — typed `FormFile` binding for `multipart/form-data` (streamed, spooled to disk, size-limited) and ASP.NET Core-shaped file responses (`file` / `file_stream` / `file_seekable_stream`) with `Content-Disposition`, `ETag`, `Last-Modified` and `Range`.
- **ORM-agnostic** — the framework does not depend on `rust-ef`; the `docbit` reference app wires it in with minimal glue.
## Architecture
The framework is split into a small set of focused crates, re-exported through the `rust-webx` umbrella crate:
```
┌──────────────────────────────────────────────────────────┐
│ rust-webx (umbrella crate) │
│ re-exports core / host / macros / spa / openapi + rust_dix│
├──────────┬──────────┬──────────┬─────────────────────────┤
│rust-webx-│rust-webx-│rust-webx-│ rust-webx-macros │
│host │core │openapi │ #[get] #[post] #[handler]│
│Host + │traits + │OpenAPI │ #[authorize] #[fromclaim]│
│pipeline │config │generation│ │
├──────────┴──────────┴──────────┴─────────────────────────┤
│ rust-webx-core │
│ IHost / IHttpContext / IMiddleware / IRequestHandler │
│ IMediator / AppOptions / Error / AppMode │
└──────────────────────┬───────────────────────────────────┘
│
┌────────────┼────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ rust-dix │ │ hyper │ │inventory │
│ (DI) │ │ (HTTP) │ │ (route │
└──────────┘ └──────────┘ └────╌─────┘
collection
```
### Crate layout
```
rust-webx/
├── Cargo.toml # workspace root (v0.10.0)
├── crates/
│ ├── core/ # rust-webx-core — traits, configuration
│ ├── host/ # rust-webx-host — Host builder, middleware
│ │ # pipeline, Trie-based router, hyper integration
│ ├── macros/ # rust-webx-macros — procedural macros
│ ├── spa/ # rust-webx-spa — static-file / SPA middleware
│ ├── openapi/ # rust-webx-openapi — OpenAPI spec generation + UI
│ └── webx/ # rust-webx — umbrella crate (re-exports)
├── docbit/ # reference app: portfolio + blog + RBAC + docs
└── dmbit/ # reference app: device & inventory management
```
> **Package vs import name**: the dependency is `rust-webx`, the code is
> `use webx::*;`. Same for the sub-crates — `rust-webx-core` → `webx_core`,
> `rust-webx-host` → `webx_host`, `rust-webx-macros` → `webx_macros`,
> `rust-webx-spa` → `webx_spa`, `rust-webx-openapi` → `webx_openapi`.
> Upgrading from 0.4 or earlier: see the
> [0.5 upgrade guide](docs/rust-webx/16-migration/upgrade-to-0.5.md).
## Request-as-Endpoint: Hello World
Declare a request, map a route onto it, implement its handler — no route table to maintain:
```rust
use webx::*;
struct HelloRequest;
#[get("/hello")]
impl IRequest<String> for HelloRequest {}
#[derive(Default)]
struct HelloHandler;
#[handler] // compile-time registration into the DI container
#[async_trait]
impl IRequestHandler<HelloRequest, String> for HelloHandler {
async fn handle(&self, _req: HelloRequest) -> Result<String> {
Ok("Hello, World! Welcome to Rust WebX.".to_string())
}
}
#[tokio::main]
async fn main() {
Host::builder()
.build()
.run()
.await
.expect("Server failed");
}
```
Routing discovery, handler resolution, middleware orchestration, and graceful shutdown are all handled automatically by `Host::build()`.
## Core Concepts
| `IRequest<TResponse>` | Generic request marker carrying the response type. `IRequest<()>` returns `204 No Content`. |
| `IRequestHandler<T, R>` | Two-type-parameter handler; `T` is the request type, `R` the response type. |
| `#[get("/path")]` | Route shortcut annotated on an `impl IRequest<T>` block; registered at compile time. |
| `#[post("/path")]` / `#[put("/path")]` / `#[delete("/path")]` | HTTP-method route shortcuts. |
| `IMediator` | Mediator; `send()` dispatches requests, `publish()` publishes events. |
| `IMiddleware` | Middleware; sequential pipeline that can short-circuit requests. |
| `IPipelineBehavior` | Mediator pipeline interceptor that wraps the request-handling chain. |
| `IEventHandler<T>` | Event handler; broadcast to all registered handlers via `publish()`. |
| `Error` | Unified error type automatically mapped to HTTP status codes. |
| `IClaims` / `IAuthenticationHandler` | JWT auth interfaces that extract identity from the Bearer token. |
| `IAuthorizationPolicy` | Legacy custom-policy interface; route-pattern authorization. |
| `IDynamicAuthorizer` | Route-level runtime authorization (DB/cache/feature flags) after static `#[authorize]` rules. |
| `IEndpointCatalog` | Runtime endpoint metadata (`all()` / `localized()`); DI-injected or `host.endpoints()`. |
| `IStringLocalizer` | Resource-key lookup for endpoint text and OpenAPI; default culture from `Culture.Default`. |
### Request lifecycle
```
HTTP Request
│
▼
HttpContext::new(req).await ← read body bytes
│
▼
MiddlewarePipeline::execute() ← middleware run in registration order;
│ each may short-circuit the response
▼
Router::match_route(ctx) ← Trie match on method + path;
│ {param} values → route_params
▼
Route matched? ──No──▶ 404 "Not Found"
│ Yes
▼
IEndpoint::handle(ctx) ← invoke handler, serialize response
▼
HttpResponse → hyper::Response ← on Err, built-in exception middleware maps status
▼
JSON: RFC 7807 application/problem+json (4xx / 5xx)
```
## Example Applications
Two full reference applications ship in this repository, demonstrating the framework, layered project structure (`contracts` / `handlers` / `domain`), JWT auth, RBAC, and SPA hosting:
| [`docbit`](docbit/) | Portfolio + blog + RBAC + a full docs site (`docs/`). Uses `rust-ef` (SQLite in dev and production). | <http://localhost:5000> |
| [`dmbit`](dmbit/) | Device & inventory management for a data-center rig (device/product/spec/stock management). SQLite-based. | <http://localhost:5100> |
Run the reference apps in development:
```bash
cargo run -p docbit-host # → http://localhost:5000
cargo run -p dmbit-host # → http://localhost:5100
```
For production deployment details, see [`docbit/PRODUCTION.md`](docbit/PRODUCTION.md).
## Quick Start
### Build
```bash
# release build of a specific application
cargo build --release -p docbit-host
cargo build --release -p dmbit-host
```
### Run a reference app
```bash
cargo run -p docbit-host # development (SQLite), http://localhost:5000
```
### Publish (bare metal)
```bash
# Linux / macOS
chmod +x docbit/publish.sh
./docbit/publish.sh /opt/docbit --production
# Windows
.\docbit\publish.ps1 -Destination D:\deploy\docbit -Production
```
Set `DATABASE_URL` and `JWT_SECRET` before starting, or edit the generated `run.sh` / `run.cmd`.
### Docker (optional)
Primary deployment is **Linux binary + static assets** via `docbit/publish.sh` (see [docbit/PRODUCTION.md](docbit/PRODUCTION.md)). Docker files are reference-only and not maintained in CI:
```bash
# standalone image (verify locally before use)
docker build -f docbit/Dockerfile .
# local stack (docbit + SQLite volume)
cp docbit/.env.example docbit/.env # edit JWT_SECRET
docker compose -f docbit/docker-compose.yml --env-file docbit/.env up --build
# → http://localhost:8100
```
## Production Readiness
| Graceful shutdown (Ctrl+C / SIGTERM) | ✅ |
| Connection drain (30s in Production) | ✅ |
| Health checks `/health` `/health/ready` (runtime probes, fail → 503) | ✅ |
| Security response headers + Request ID | ✅ on by default |
| JWT production fail-fast | ✅ |
| CORS `*` production fail-fast | ✅ |
| OpenAPI UI | Development mode only |
| Rate limiting / compression | app-layer opt-in (`use_middleware`) |
| File uploads (streamed, disk-spooled, size-limited) | ✅ |
| File downloads (`File(...)`-style, streamed, `Content-Disposition`, `ETag`, `Range`, `304`, `HEAD`) | ✅ |
| TLS | ✅ via `App.Urls` + `Tls.CertPath/KeyPath` |
## Environment Variables
- `APP_ENV` — application environment (`Development` / `Production`).
- `WEBX_APP_BASE` — application base directory.
- `JWT_SECRET` — JWT signing secret (≥32 chars; requires `APP_ENV=Production`).
- `APP__Jwt__Secret` — overrides `JWT_SECRET`.
- `DATABASE_URL` — optional DB override (docbit defaults to SQLite `app.db` beside the binary).
- `APP__*` — inline JSON override of any `appsettings.json` key (e.g. `APP__App__Urls`, `APP__Cors__Origins`).
See the configuration chapter under `docs/rust-webx/` for the full variable reference.
## Error Mapping
| `Error::NotFound(msg)` | 404 | Resource not found |
| `Error::Validation(msg)` | 400 | Validation failure |
| `Error::Serialization(e)` | 400 | (De)serialization error |
| `Error::Http(msg)` | 400 | HTTP protocol error (incl. 401 Unauthorized, 403 Forbidden) |
| `Error::Di(msg)` | 500 | DI container error |
| `Error::Internal(msg)` | 500 | Internal error |
| `Error::Message(msg)` | 500 | Generic error message |
| `Error::Routing(msg)` | 404 | Routing error |
## Documentation
- **rust-webx developer's manual** — a progressive-disclosure book under [`docs/rust-webx/`](docs/rust-webx/INDEX.md) (16 chapters, Chinese): introduction, quick start, architecture, DI & lifecycle, middleware, mediator & events, auth & security, configuration, production, project structure, extensibility, best practices, case study and migration guides.
- **rust-ef reference** — the ORM handbook under [`docs/rust-ef/`](docs/rust-ef/INDEX.md), used by the `docbit` example.
- The `docbit` reference app also serves these docs as a live website (`GET /api/docs/rust-webx/...`).
## License
[MIT](LICENSE)