Web frameworks
Warp filters that validate request data with vld schemas, plus a JSON rejection handler.

Validation filters for Warp 0.3 and 0.4. Shared rules (status codes, error body, coercion, headers and cookies) are on the web adapters overview.

Install

Cargo.toml
[dependencies]
vld = "0.5"
vld-warp = "0.5"
warp = { version = "0.4", features = ["server"] }

The dependency is warp >=0.3, <0.5; there are no version features, Cargo picks the warp you use. MSRV 1.85, see MSRV.

Example

use vld_warp::prelude::*;
use warp::Filter;

vld::schema! {
    #[derive(Debug, Clone)]
    pub struct CreateUser {
        pub name: String  => vld::string().min(2),
        pub email: String => vld::string().email(),
    }
}

vld::schema! {
    #[derive(Debug, Clone)]
    pub struct UserId {
        pub id: i64 => vld::number().int().min(1),
    }
}

#[tokio::main]
async fn main() {
    let create = warp::post()
        .and(warp::path("users"))
        .and(warp::path::end())
        .and(vld_json::<CreateUser>())
        .map(|u: CreateUser| warp::reply::json(&serde_json::json!({ "name": u.name })));

    let get = warp::get()
        .and(warp::path("users"))
        .and(vld_param::<UserId>("id"))
        .and(warp::path::end())
        .map(|p: UserId| format!("user #{}", p.id));

    warp::serve(create.or(get).recover(handle_rejection))
        .run(([127, 0, 0, 1], 3030))
        .await;
}

API

ItemPurpose
vld_json::<T>()JSON body; 415 without a JSON Content-Type
vld_json_limit::<T>(bytes)Same with an explicit body limit
vld_form::<T>() / vld_form_limit::<T>(bytes)application/x-www-form-urlencoded body
vld_query::<T>()Query string (an absent query is {})
vld_param::<T>(name)One path segment, wrapped as {name: value}
vld_path::<T>(&names)All remaining segments, mapped to names in order
validate_path_params::<T>(&pairs)Validate (name, raw) pairs you extracted yourself
vld_headers::<T>()Request headers
vld_cookie::<T>()Cookies from every Cookie header
VldRejectionCustom rejection: status(), body(), error()
handle_rejectionRecovery function for .recover()
preludeAll of the above plus vld::prelude::*

Path values are percent-decoded before coercion.

Rejection handling

Without .recover(handle_rejection) a VldRejection reaches warp's default handler, which does not know it. handle_rejection answers:

RejectionStatus
VldRejectionits own status (422 / 413 / 415) and JSON body
PayloadTooLarge413
UnsupportedMediaType415
LengthRequired411
MissingHeader, InvalidHeader, MissingCookie, InvalidQuery400
MethodNotAllowed405
not found404
anything else500

Non-vld rejections get {"error": "<message>"}.

Gotchas

  • Bodies are read as a stream and stop at the limit (2 MiB by default), with or without Content-Length, so chunked uploads are limited too.
  • vld_path needs exactly as many remaining segments as names; any other count is a 404. Use vld_param or validate_path_params when static and dynamic segments are interleaved.
  • Since 0.5 unknown rejections are 500, not 404.