Validation
Oxide’s validators wrap the validator
crate and expose its rules through a #[request] attribute. A request
struct describes the expected payload, and the framework returns a 422
JSON envelope on failure without invoking the handler.
Declare the request
use oxide_http::request;
#[request]pub struct StoreTodoRequest { #[validate(required, length(min = 1, max = 200))] pub title: Option<String>, #[validate] pub done: Option<bool>,}- Every field is
Option<T>so the validator can distinguish “missing” from “present but invalid”. #[validate(required)]is your “not null” rule.#[validate(length / email / range / regex / ...)]— any validator combinator works.
Using it in a handler
Take the request struct as the handler’s argument. The framework runs validation before calling you.
pub async fn store(form: StoreTodoRequest) -> Response { // Getting here means validation passed. Required fields are Some. let title = form.title.expect("validated as Some"); // ... Response::text(format!("stored {title}"))}Error envelope
A failing request returns 422 with:
{ "message": "ข้อมูลไม่ถูกต้อง", "errors": [ { "ref": "title", "type": "required", "desc": ["ต้องระบุ title"] } ]}Every offending field surfaces — not just the first.
Query-string parse
request.query::<T>() parses the URL query into T using the same
envelope shape on failure:
#[derive(serde::Deserialize)]pub struct PageQuery { #[serde(default = "default_page")] pub page: u32,}
pub async fn list(req: Request) -> Response { let query: PageQuery = match req.query() { Ok(q) => q, Err(resp) => return resp, }; // ... Response::text(format!("page {}", query.page))}