Web frameworks
Drop-in replacements for web::Json, web::Query, web::Path and web::Form, plus header and cookie extractors. Shared behaviour (status codes, error body, coercion, header/cookie rules) is in the web adapters overview.
Install
Cargo.toml
[dependencies]
vld = "0.5"
vld-actix = "0.5"
actix-web = "4"
No features. Works with actix-web 4.4+. MSRV 1.85 (see MSRV).
Example
main.rs
use actix_web::{web, App, HttpResponse, HttpServer};
use vld_actix::{VldBodyLimit, VldJson, VldPath, VldQuery};
vld::schema! {
#[derive(Debug)]
pub struct CreateUser {
pub name: String => vld::string().min(2).max(50),
pub email: String => vld::string().email(),
}
}
vld::schema! {
#[derive(Debug)]
pub struct UserPath {
pub id: i64 => vld::number().int().min(1),
}
}
vld::schema! {
#[derive(Debug)]
pub struct Search {
pub q: String => vld::string().min(1),
pub page: Option<i64> => vld::number().int().min(1).optional(),
}
}
async fn create_user(user: VldJson<CreateUser>) -> HttpResponse {
HttpResponse::Created().body(format!("created {}", user.name))
}
async fn get_user(path: VldPath<UserPath>) -> HttpResponse {
HttpResponse::Ok().body(format!("user #{}", path.id))
}
async fn search(q: VldQuery<Search>) -> HttpResponse {
HttpResponse::Ok().body(format!("{} (page {})", q.q, q.page.unwrap_or(1)))
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
App::new()
.app_data(VldBodyLimit(8 * 1024 * 1024)) // optional, default 2 MiB
.route("/users", web::post().to(create_user))
.route("/users/{id}", web::get().to(get_user))
.route("/search", web::get().to(search))
})
.bind("127.0.0.1:8080")?
.run()
.await
}
The full example is crates/vld-actix/examples/actix_basic.rs (cargo run -p vld-actix --example actix_basic).
API
| Item | Description |
|---|---|
VldJson<T> | JSON body (requires a JSON Content-Type) |
VldQuery<T> | Query string |
VldPath<T> | Path parameters, including custom patterns such as {id:\d+} |
VldForm<T> | application/x-www-form-urlencoded body |
VldHeaders<T> | Request headers (X-Request-Id → x_request_id) |
VldCookie<T> | Cookies from all Cookie headers |
VldActixError | Error of every extractor (ResponseError): status(), body(), error() |
VldBodyLimit | Body size limit for VldJson / VldForm, registered with app_data (re-exported from vld-http-common) |
prelude | Everything above plus vld::prelude::* |
Extractors implement Deref / DerefMut to T, so user.name works directly; user.0 or let VldJson(user) = user; moves the value out.
Gotchas
- Body limit:
VldJsonandVldFormread the payload themselves; actix'sJsonConfigandPayloadConfigdo not apply. RegisterVldBodyLimit(n)withApp::app_data(or on a scope / resource); not wrapped inweb::Data. - Compressed bodies (
Content-Encoding: gzip,br, ...) are decoded before validation; the limit applies to the decoded size. - Actix keeps
%2F,%25and%2Bencoded inmatch_info;VldPathdecodes them, so/files/a%2Fbgives"a/b".