221 lines
6.4 KiB
Rust
221 lines
6.4 KiB
Rust
//! # Basic extractors validation
|
|
//!
|
|
//! * `Path`
|
|
//! * `Query`
|
|
//! * `Form`
|
|
//! * `Json`
|
|
|
|
use axum::extract::{Path, Query};
|
|
use axum::http::StatusCode;
|
|
use axum::routing::{get, post};
|
|
use axum::{Form, Json, Router};
|
|
use axum_valid::{Valid, VALIDATION_ERROR_STATUS};
|
|
use serde::{Deserialize, Serialize};
|
|
use serde_json::json;
|
|
use std::net::SocketAddr;
|
|
use validator::Validate;
|
|
|
|
mod route {
|
|
pub const PATH: &str = "/path/:v0/:v1";
|
|
pub const QUERY: &str = "/query";
|
|
pub const FORM: &str = "/form";
|
|
pub const JSON: &str = "/json";
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn main() -> anyhow::Result<()> {
|
|
let router = Router::new()
|
|
.route(route::PATH, get(extract_path))
|
|
.route(route::QUERY, get(extract_query))
|
|
.route(route::FORM, post(extract_form))
|
|
.route(route::JSON, post(extract_json));
|
|
|
|
let server = axum::Server::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16)))
|
|
.serve(router.into_make_service());
|
|
let server_addr = server.local_addr();
|
|
println!("Axum server address: {}.", server_addr);
|
|
|
|
let (server_guard, close) = tokio::sync::oneshot::channel::<()>();
|
|
let server_handle = tokio::spawn(server.with_graceful_shutdown(async move {
|
|
let _ = close.await;
|
|
}));
|
|
|
|
let server_url = format!("http://{}", server_addr);
|
|
let client = reqwest::Client::default();
|
|
|
|
let valid_parameters = Parameters {
|
|
v0: 5,
|
|
v1: "0123456789".to_string(),
|
|
};
|
|
|
|
let invalid_parameters = Parameters {
|
|
v0: 6,
|
|
v1: "01234567890".to_string(),
|
|
};
|
|
|
|
// Valid<Path<...>>
|
|
let valid_path_response = client
|
|
.get(format!(
|
|
"{}/path/{}/{}",
|
|
server_url, valid_parameters.v0, valid_parameters.v1
|
|
))
|
|
.send()
|
|
.await?;
|
|
assert_eq!(valid_path_response.status(), StatusCode::OK);
|
|
|
|
let invalid_path_response = client
|
|
.get(format!("{}/path/invalid/path", server_url))
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_path_response.status(), StatusCode::BAD_REQUEST);
|
|
|
|
let invalid_path_response = client
|
|
.get(format!(
|
|
"{}/path/{}/{}",
|
|
server_url, invalid_parameters.v0, invalid_parameters.v1
|
|
))
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_path_response.status(), VALIDATION_ERROR_STATUS);
|
|
#[cfg(feature = "into_json")]
|
|
assert!(invalid_path_response
|
|
.json::<serde_json::Value>()
|
|
.await
|
|
.is_ok());
|
|
println!("Valid<Path<...>> works.");
|
|
|
|
// Valid<Query<...>>
|
|
let query_url = format!("{}{}", server_url, route::QUERY);
|
|
let valid_query_response = client
|
|
.get(&query_url)
|
|
.query(&valid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(valid_query_response.status(), StatusCode::OK);
|
|
|
|
let invalid_query_response = client
|
|
.get(&query_url)
|
|
.query(&[("invalid", "query")])
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_query_response.status(), StatusCode::BAD_REQUEST);
|
|
|
|
let invalid_query_response = client
|
|
.get(&query_url)
|
|
.query(&invalid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_query_response.status(), VALIDATION_ERROR_STATUS);
|
|
#[cfg(feature = "into_json")]
|
|
assert!(invalid_query_response
|
|
.json::<serde_json::Value>()
|
|
.await
|
|
.is_ok());
|
|
println!("Valid<Query<...>> works.");
|
|
|
|
// Valid<Form<...>>
|
|
let form_url = format!("{}{}", server_url, route::FORM);
|
|
let valid_form_response = client
|
|
.post(&form_url)
|
|
.form(&valid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(valid_form_response.status(), StatusCode::OK);
|
|
|
|
let invalid_form_response = client
|
|
.post(&form_url)
|
|
.form(&[("invalid", "form")])
|
|
.send()
|
|
.await?;
|
|
assert_eq!(
|
|
invalid_form_response.status(),
|
|
StatusCode::UNPROCESSABLE_ENTITY
|
|
);
|
|
|
|
let invalid_form_response = client
|
|
.post(&form_url)
|
|
.form(&invalid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_form_response.status(), VALIDATION_ERROR_STATUS);
|
|
#[cfg(feature = "into_json")]
|
|
assert!(invalid_form_response
|
|
.json::<serde_json::Value>()
|
|
.await
|
|
.is_ok());
|
|
println!("Valid<Form<...>> works.");
|
|
|
|
// Valid<Json<...>>
|
|
let json_url = format!("{}{}", server_url, route::JSON);
|
|
let valid_json_response = client
|
|
.post(&json_url)
|
|
.json(&valid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(valid_json_response.status(), StatusCode::OK);
|
|
|
|
let invalid_json_response = client
|
|
.post(&json_url)
|
|
.json(&json!({"invalid": "json"}))
|
|
.send()
|
|
.await?;
|
|
assert_eq!(
|
|
invalid_json_response.status(),
|
|
StatusCode::UNPROCESSABLE_ENTITY
|
|
);
|
|
|
|
let invalid_json_response = client
|
|
.post(&json_url)
|
|
.json(&invalid_parameters)
|
|
.send()
|
|
.await?;
|
|
assert_eq!(invalid_json_response.status(), VALIDATION_ERROR_STATUS);
|
|
#[cfg(feature = "into_json")]
|
|
assert!(invalid_json_response
|
|
.json::<serde_json::Value>()
|
|
.await
|
|
.is_ok());
|
|
println!("Valid<Json<...>> works.");
|
|
|
|
drop(server_guard);
|
|
server_handle.await??;
|
|
Ok(())
|
|
}
|
|
|
|
// Implement `Deserialize` and `Validate` for `Parameters`,
|
|
// then `Valid` will work as you expect.
|
|
#[derive(Debug, Deserialize, Serialize, Validate)]
|
|
struct Parameters {
|
|
#[validate(range(min = 5, max = 10))]
|
|
v0: i32,
|
|
#[validate(length(min = 1, max = 10))]
|
|
v1: String,
|
|
}
|
|
|
|
async fn extract_path(Valid(Path(parameters)): Valid<Path<Parameters>>) -> StatusCode {
|
|
validate_again(parameters)
|
|
}
|
|
|
|
async fn extract_query(Valid(Query(parameters)): Valid<Query<Parameters>>) -> StatusCode {
|
|
validate_again(parameters)
|
|
}
|
|
|
|
async fn extract_form(Valid(Form(parameters)): Valid<Form<Parameters>>) -> StatusCode {
|
|
validate_again(parameters)
|
|
}
|
|
|
|
async fn extract_json(Valid(Json(parameters)): Valid<Json<Parameters>>) -> StatusCode {
|
|
validate_again(parameters)
|
|
}
|
|
|
|
fn validate_again(parameters: Parameters) -> StatusCode {
|
|
// The `Valid` extractor has validated the `parameters` once,
|
|
// it should have returned `400 BAD REQUEST` if the `parameters` were invalid,
|
|
// Let's validate them again to check if the `Valid` extractor works well.
|
|
// If it works properly, this function will never return `500 INTERNAL SERVER ERROR`
|
|
match parameters.validate() {
|
|
Ok(_) => StatusCode::OK,
|
|
Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
|
}
|
|
}
|