rust-webx 1.1.1

rust-webx — A Rust WebApi framework inspired by ASP.NET Core
Documentation

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.

Crates.io Version License: MIT CI Rust


Table of Contents


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 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 (FileResult::path / ::stream / ::seekable_stream, ContentResult) 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 (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 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.

Request-as-Endpoint: Hello World

Declare a request, map a route onto it, implement its handler — no route table to maintain:

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(&mut self, _req: HelloRequest) -> ActionResult<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

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:

cargo run -p docbit-host     # → http://localhost:5000
cargo run -p dmbit-host      # → http://localhost:5100

For production deployment details, see docbit/PRODUCTION.md.

Quick Start

Build

# release build of a specific application
cargo build --release -p docbit-host
cargo build --release -p dmbit-host

Run a reference app

cargo run -p docbit-host     # development (SQLite), http://localhost:5000

Publish (bare metal)

# 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). Docker files are reference-only and not maintained in CI:

# 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

Development

Run the full suite from the workspace root:

cargo test --workspace --no-fail-fast

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; 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 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 ResponseData with the IActionResult family; follow the 1.0.2 → 1.1.0 migration guide.
  • rust-ef reference — the ORM handbook lives with the rust-ef crate (see its docs.rs documentation), used by the docbit example.
  • The docbit reference app also serves these docs as a live website (GET /api/docs/rust-webx/...).

License

MIT