Error Handling & Validation
Treat serde parsing as the first security gate - map errors clearly and validate semantics after syntax succeeds.
Busca en todas las páginas de la documentación
Treat serde parsing as the first security gate - map errors clearly and validate semantics after syntax succeeds.
Quick-reference recipe card - copy-paste ready.
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Input {
#[serde(deserialize_with = "bounded_string")]
name: String,
}
fn bounded_string<'de, D: serde::Deserializer<'de>>(d: D) -> Result<String, D::Error> {
let s = String::deserialize(d)?;
if s.len() > 256 { return Err(serde::de::Error::custom("name too long")); }
Ok(s)
}When to reach for this: Every public API, webhook, or upload endpoint parsing JSON from untrusted clients.
use serde::Deserialize;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct Signup {
email: String,
age: u8,
}
#[derive(Debug)]
enum ParseError {
Serde(serde_json::Error),
Business(&'static str),
}
fn parse_signup(raw: &str) -> Result<Signup, ParseError> {
let body: Signup = serde_json::from_str(raw).map_err(ParseError::Serde)?;
if !body.email.contains('@') {
return Err(ParseError::Business("invalid email"));
}
if body.age < 18 {
return Err(ParseError::Business("too young"));
}
Ok(body)
}
fn main() {
let ok = parse_signup(r#"{"email":"a@b.co","age":21}"#).unwrap();
assert_eq!(ok.email, "a@b.co");
let extra = parse_signup(r#"{"email":"a@b.co","age":21,"admin":true}"#);
assert!(extra.is_err());
}What this demonstrates:
deny_unknown_fields rejects unexpected JSON keys early.| Stage | Checks |
|---|---|
| Size limit | Max body bytes before parse |
| Serde | Types, required fields, unknown keys |
| Semantic | Email, ranges, referential integrity |
| Authz | Caller allowed to set fields |
// Map serde errors to HTTP 400
fn bad_request(err: serde_json::Error) -> ApiError {
ApiError::BadRequest(err.to_string())
}code + safe message.RequestBodyLimitLayer in Axum.u8 fail serde - good. Fix: Use appropriate widths; document limits.deny_unknown_fields + strict producers.
| Alternative | Use When | Don't Use When |
|---|---|---|
validator crate | Declarative field rules | Custom wire-only checks |
| JSON Schema | External contract enforcement | Internal-only APIs |
garde | Compile-time validation | Already on validator |
400 malformed JSON; 422 valid JSON failing business rules - document team standard.
Log truncated body hash and error, not full PII payload.
serde_json::Value first for probing - loses strictness.
Validate filenames separately from serde parse.
Limit JSON depth/recursion in parser or reject huge nested Value.
Same mapping pattern - wrap in domain error type.
Collect per-row errors; do not fail entire file on one bad row if product allows.
Different error model - see gRPC page.
cargo fuzz on deserialize entry points finds panics and crashes.
Table tests for too-long strings, unknown fields, type mismatches.
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.
Revisado por Chris St. John·Última actualización: 19 jul 2026