Extractors for Rama 0.4 WebService handlers, plus a ValidateJsonLayer for proxy and gateway service stacks. Shared rules (status codes, error body, coercion, headers and cookies) are on the web adapters overview.
Install
[dependencies]
vld = "0.5"
vld-rama = "0.5"
rama = { version = "0.4", features = ["http"] }
The crate has no features of its own; it pulls rama with http only. MSRV 1.96 (Rama's own), see MSRV.
Example
use rama::http::service::web::WebService;
use vld_rama::{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 Search {
pub q: String => vld::string().min(1),
pub page: Option<i64> => vld::number().int().min(1).optional(),
}
}
vld::schema! {
#[derive(Debug)]
pub struct UserPath {
pub id: i64 => vld::number().int().min(1),
}
}
async fn create(VldJson(user): VldJson<CreateUser>) -> String {
format!("created {}", user.name)
}
async fn search(VldQuery(q): VldQuery<Search>) -> String {
format!("q={} page={}", q.q, q.page.unwrap_or(1))
}
async fn get_user(VldPath(p): VldPath<UserPath>) -> String {
format!("user #{}", p.id)
}
let svc = WebService::default()
.with_post("/users", create)
.with_get("/search", search)
.with_get("/users/{id}", get_user);
As a layer
ValidateJsonLayer<T> validates JSON bodies before the inner service runs and stores the value as Validated<T> in the request extensions. Read it with validated::<T>(&req):
use rama::http::{Body, Request, Response};
use rama::{Layer, Service};
use vld_rama::{validated, ValidateJsonLayer};
#[derive(Clone)]
struct Backend;
impl Service<Request> for Backend {
type Output = Response;
type Error = std::convert::Infallible;
async fn serve(&self, req: Request) -> Result<Self::Output, Self::Error> {
// None for non-JSON requests: the layer lets them through unread.
let name = validated::<CreateUser>(&req).map_or("-".into(), |u| u.name);
Ok(Response::new(Body::from(name)))
}
}
let gateway = ValidateJsonLayer::<CreateUser>::new().layer(Backend);
CreateUser must be Clone + Debug + Send + Sync + 'static for the layer.
API
| Item | Purpose |
|---|---|
VldJson<T> | JSON body (FromRequest / FromRequestBody); 415 without a JSON Content-Type |
VldQuery<T> | Query string, coerced |
VldPath<T> | Route parameters (/users/{id}), coerced; {} on a route without parameters |
VldForm<T> | application/x-www-form-urlencoded body (never the query string) |
VldHeaders<T> | Request headers |
VldCookie<T> | Cookies from every Cookie header |
ValidateJsonLayer<T> | Layer; new() (2 MiB) or with_limit(bytes) |
ValidateJsonService<S, T> | The service the layer produces |
Validated<T> | Extension wrapper holding the validated value |
validated::<T>(&req) | Option<T> from the extensions |
VldBodyLimit(usize) | Request extension that sets the extractor body limit |
VldRejection | Rejection: status(), body(), error(); implements IntoResponse |
prelude | All of the above plus vld::prelude::* |
All extractors deref to T.
Body limit
Extractors read at most 2 MiB. Set another limit per router with the VldBodyLimit extension:
use rama::layer::AddInputExtensionLayer;
use rama::Layer;
use vld_rama::VldBodyLimit;
let svc = AddInputExtensionLayer::new(VldBodyLimit(64 * 1024)).into_layer(
WebService::default().with_post("/users", create),
);
The layer takes its own limit: ValidateJsonLayer::<T>::with_limit(64 * 1024).
Gotchas
validated()returnsOption<T>.- The layer passes requests without a JSON
Content-Typethrough without reading the body, so a handler behind it must handleNone. - An over-limit body is 413 only when the error is Rama's
LengthLimitError; any other body read error is a 422.