English | 简体中文
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.
Table of Contents
- What is rust-webx
- Key Features
- Architecture
- Request-as-Endpoint: Hello World
- Core Concepts
- Example Applications
- Quick Start
- Development
- Production Readiness
- Environment Variables
- Error Mapping
- Documentation
- 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:
| Pain point | How rust-webx solves it |
|---|---|
| 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), 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 inHost::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: IdentityUserbinds 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 —
IEndpointCatalogexposes 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-wideCulture.Defaultselects the language, with.jsonresources orinclude_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
FormFilebinding formultipart/form-data(streamed, spooled to disk, size-limited) and ASP.NET Core-shaped file responses (FileResult::path/::stream/::seekable_stream,ContentResult) withContent-Disposition,ETag,Last-ModifiedandRange. - ORM-agnostic — the framework does not depend on
rust-ef; thedocbitreference 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 (v1.1.1)
├── 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 isuse 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.
Request-as-Endpoint: Hello World
Declare a request, map a route onto it, implement its handler — no route table to maintain:
use *;
;
;
// compile-time registration into the DI container
async
Routing discovery, handler resolution, middleware orchestration, and graceful shutdown are all handled automatically by Host::build().
Core Concepts
| Concept | Description |
|---|---|
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:
| Application | Description | Dev URL |
|---|---|---|
docbit |
Portfolio + blog + RBAC + a full docs site (docs/). Uses rust-ef (SQLite in dev and production). |
http://localhost:5000 |
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:
For production deployment details, see docbit/PRODUCTION.md.
Quick Start
Build
# release build of a specific application
Run a reference app
Publish (bare metal)
# Linux / macOS
# Windows
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). Docker files are reference-only and not maintained in CI:
# standalone image (verify locally before use)
# local stack (docbit + SQLite volume)
# → http://localhost:8100
Development
Run the full suite from the workspace root:
Disk usage (large Windows checkouts)
target/ is disposable, but Cargo never deletes artifacts that belong to an old
fingerprint, so every full rebuild (toolchain bump, Cargo.lock update,
RUSTFLAGS change, switching feature sets) leaves its whole universe behind.
The dev/test profiles in Cargo.toml keep new builds small
(no debug info for dependencies, line tables only for workspace crates, no
incremental sessions). If target/ has already grown, report and reclaim it:
.\scripts\clean-target.ps1 # report sizes, change nothing
.\scripts\clean-target.ps1 -Incremental # drop the incremental compile cache only
.\scripts\clean-target.ps1 -All # cargo clean (everything is rebuildable)
For a debugging or fast-edit session, override the committed profile for that shell only:
$env:CARGO_PROFILE_DEV_DEBUG = '2' # full debug info for workspace crates
$env:CARGO_PROFILE_DEV_INCREMENTAL = 'true' # incremental compilation back on
Production Readiness
| Capability | Status |
|---|---|
| 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 (FileResult, 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; requiresAPP_ENV=Production).APP__Jwt__Secret— overridesJWT_SECRET.DATABASE_URL— optional DB override (docbit defaults to SQLiteapp.dbbeside the binary).APP__*— inline JSON override of anyappsettings.jsonkey (e.g.APP__App__Urls,APP__Cors__Origins).
See the configuration chapter under docs/rust-webx/ for the full variable reference.
Error Mapping
Error variant |
HTTP status | Meaning |
|---|---|---|
Error::Http(msg) |
400 | HTTP protocol error |
Error::Status(code, msg) |
code |
Explicit status code |
Error::Unauthorized(msg) |
401 | Authentication failed / missing token |
Error::Forbidden(msg) |
403 | Insufficient permissions |
Error::Di(msg) |
500 | DI container error |
Error::Routing(msg) |
404 | Routing error |
Error::Serialization(e) |
400 | Request (de)serialization error |
Error::ResponseSerialization(e) |
500 | Response serialization failed (server-side DTO defect) |
Error::State(msg) |
500 | Server-side state / programming error |
Error::Internal(msg) |
500 | Internal error |
Error::Message(msg) |
500 | Generic error message |
Error::Validation(msg) |
400 | Validation failure |
Error::NotFound(msg) |
404 | Resource not found |
Error::Conflict(msg) |
409 | Optimistic concurrency / state conflict |
Error::PayloadTooLarge(msg) |
413 | Request body or uploaded file too large |
Error::UnsupportedMediaType(msg) |
415 | Unsupported request content type |
Error::Problem(details) |
details.status |
Field-level binding/validation problem carrying errors |
Documentation
- rust-webx developer's manual — a progressive-disclosure book under
docs/rust-webx/(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. - Upgrading from 1.0.x — 1.1.0 replaced
ResponseDatawith theIActionResultfamily; follow the 1.0.2 → 1.1.0 migration guide. - rust-ef reference — the ORM handbook lives with the
rust-efcrate (see its docs.rs documentation), used by thedocbitexample. - The
docbitreference app also serves these docs as a live website (GET /api/docs/rust-webx/...).