salvo-data-extraction
Extract and validate data from requests including JSON, forms, query parameters, and path parameters. Use for handling user input and API payloads.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Extract and validate data from requests including JSON, forms, query parameters, and path parameters. Use for handling user input and API payloads.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
Guide for using RBatis v4 Rust ORM framework. Use when implementing database CRUD operations, writing dynamic SQL with py_sql or html_sql macros, configuring database connections, managing transactions, implementing interceptors, syncing table structures, or integrating RBatis into Genies microservices. Also use when the user asks about RBatis usage patterns, query building, or database operations in Rust.
Genies 前后端接口规范。Use when designing API contracts, defining field naming conventions, date/time formats, response models, pagination, error handling, or ID strategies between Genies backend and frontend (Web/Android/OHOS).
Guide for using flyway-rs database migration framework with RBatis. Use when implementing database schema migrations, managing SQL changelog files, configuring migration runners, handling multi-database migrations, or integrating database versioning into Genies microservices.
Guide for developing Rust microservices using Genies framework following Java DDD layering principles. Use when creating new microservices, designing aggregate roots, implementing domain events, organizing service layers, setting up Flyway migrations, or structuring a DDD-based Genies project.
Genies framework unified skill hub. Use when you need to find the right skill for any Genies framework task, including authentication, authorization, caching, configuration, database, DDD microservices, Dapr messaging, macros, K8s deployment, testing, API conventions, gateway proxy, or Salvo web framework features. Also use when the user asks about Genies framework capabilities, asks which skill to use, or wants a quick overview of available Genies skills.
Guide for using the Genies Rust microservice framework with DDD and Dapr. Use when developing with Genies, creating aggregates, domain events, Dapr subscriptions, Casbin field-level permissions, configuration management, or when the user asks about Genies framework usage patterns.
| name | salvo-data-extraction |
| description | Extract and validate data from requests including JSON, forms, query parameters, and path parameters. Use for handling user input and API payloads. |
| version | 0.89.3 |
| tags | ["data","extraction","json","form","query"] |
This skill helps extract and validate data from HTTP requests in Salvo applications.
For simple cases, extract directly from Request:
use salvo::prelude::*;
#[handler]
async fn handler(req: &mut Request) -> String {
// Query parameter
let name = req.query::<String>("name").unwrap_or_default();
// Path parameter (requires route like /users/{id})
let id = req.param::<i64>("id").unwrap();
// Header
let token = req.header::<String>("Authorization");
// Parse JSON body
let body: UserData = req.parse_json().await.unwrap();
// Parse form data
let form: LoginForm = req.parse_form().await.unwrap();
// Parse query parameters as struct
let pagination: Pagination = req.parse_queries().unwrap();
format!("Processed request")
}
use salvo::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[handler]
async fn create_user(body: JsonBody<CreateUser>) -> StatusCode {
let user = body.into_inner();
println!("Name: {}, Email: {}", user.name, user.email);
StatusCode::CREATED
}
The Extractible derive macro enables automatic data extraction from requests.
use salvo::prelude::*;
use serde::Deserialize;
#[derive(Extractible, Deserialize, Debug)]
#[salvo(extract(default_source(from = "body")))]
struct CreateUser {
name: String,
email: String,
}
#[handler]
async fn create_user(user: CreateUser) -> String {
format!("Created user: {:?}", user)
}
#[derive(Extractible, Deserialize)]
#[salvo(extract(default_source(from = "body")))]
struct UserData {
name: String,
email: String,
}
#[derive(Extractible, Deserialize)]
#[salvo(extract(default_source(from = "query")))]
struct Pagination {
page: Option<u32>,
per_page: Option<u32>,
}
#[handler]
async fn list_items(query: Pagination) -> String {
let page = query.page.unwrap_or(1);
let per_page = query.per_page.unwrap_or(20);
format!("Page {} with {} items", page, per_page)
}
#[derive(Extractible, Deserialize)]
#[salvo(extract(default_source(from = "param")))]
struct UserId {
id: i64,
}
#[handler]
async fn show_user(params: UserId) -> String {
format!("User ID: {}", params.id)
}
#[derive(Extractible, Deserialize)]
#[salvo(extract(default_source(from = "body"), default_format = "form"))]
struct LoginForm {
username: String,
password: String,
}
#[handler]
async fn login(form: LoginForm) -> Result<String, StatusError> {
Ok(format!("Login: {}", form.username))
}
Extract from multiple sources simultaneously:
#[derive(Extractible, Deserialize)]
struct UpdateUser {
#[salvo(extract(source(from = "param")))]
id: i64,
#[salvo(extract(source(from = "body")))]
name: String,
#[salvo(extract(source(from = "body")))]
email: String,
}
#[handler]
async fn update_user(data: UpdateUser) -> StatusCode {
// data.id from path, name and email from body
println!("Update user {}: {} {}", data.id, data.name, data.email);
StatusCode::OK
}
Extract data from Depot that was injected by middleware:
use salvo::prelude::*;
use serde::{Deserialize, Serialize};
#[handler]
async fn inject_user(depot: &mut Depot) {
depot.insert("user_id", 123i64);
depot.insert("username", "alice".to_string());
}
#[derive(Serialize, Deserialize, Extractible, Debug)]
#[salvo(extract(default_source(from = "depot")))]
struct UserContext {
user_id: i64,
username: String,
}
#[handler]
async fn protected_handler(user: UserContext) -> String {
format!("Hello {}, your ID is {}", user.username, user.user_id)
}
use salvo::prelude::*;
use serde::Deserialize;
use validator::Validate;
#[derive(Extractible, Deserialize, Validate)]
#[salvo(extract(default_source(from = "body")))]
struct CreateUser {
#[validate(length(min = 1, max = 100))]
name: String,
#[validate(email)]
email: String,
#[validate(range(min = 18, max = 120))]
age: u8,
}
#[handler]
async fn create_user(user: CreateUser) -> Result<StatusCode, StatusError> {
if let Err(errors) = user.validate() {
return Err(StatusError::bad_request().brief(errors.to_string()));
}
Ok(StatusCode::CREATED)
}
use salvo::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct Address {
street: String,
city: String,
country: String,
}
#[derive(Extractible, Deserialize)]
#[salvo(extract(default_source(from = "body")))]
struct CreateUserWithAddress {
name: String,
email: String,
address: Address,
}
#[handler]
async fn create_user(data: CreateUserWithAddress) -> Result<String, StatusError> {
Ok(format!("User {} from {}", data.name, data.address.city))
}
use salvo::prelude::*;
use serde::Deserialize;
#[derive(Deserialize)]
struct CreateUser {
name: String,
email: String,
}
#[handler]
async fn create_user(req: &mut Request, res: &mut Response) {
match req.parse_json::<CreateUser>().await {
Ok(user) => {
res.render(Json(serde_json::json!({
"success": true,
"user": {"name": user.name, "email": user.email}
})));
}
Err(e) => {
res.status_code(StatusCode::BAD_REQUEST);
res.render(Json(serde_json::json!({
"error": format!("Invalid JSON: {}", e)
})));
}
}
}
#[handler]
async fn handler(req: &mut Request) -> Result<String, StatusError> {
let auth = req.header::<String>("Authorization")
.ok_or_else(|| StatusError::unauthorized())?;
let content_type = req.header::<String>("Content-Type");
Ok(format!("Auth: {}", auth))
}
use salvo::prelude::*;
use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Deserialize, Validate)]
struct CreateUser {
#[validate(length(min = 1, max = 100))]
name: String,
#[validate(email)]
email: String,
}
#[derive(Deserialize)]
struct Pagination {
page: Option<u32>,
per_page: Option<u32>,
}
#[derive(Serialize)]
struct User {
id: i64,
name: String,
email: String,
}
#[handler]
async fn list_users(req: &mut Request) -> Json<Vec<User>> {
let pagination: Pagination = req.parse_queries().unwrap_or(Pagination {
page: Some(1),
per_page: Some(20),
});
Json(vec![User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
}])
}
#[handler]
async fn create_user(body: JsonBody<CreateUser>) -> Result<StatusCode, StatusError> {
let user = body.into_inner();
if let Err(e) = user.validate() {
return Err(StatusError::bad_request().brief(e.to_string()));
}
Ok(StatusCode::CREATED)
}
#[handler]
async fn get_user(req: &mut Request) -> Result<Json<User>, StatusError> {
let id = req.param::<i64>("id")
.ok_or_else(|| StatusError::bad_request())?;
Ok(Json(User {
id,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
}))
}
#[tokio::main]
async fn main() {
let router = Router::new()
.push(
Router::with_path("users")
.get(list_users)
.post(create_user)
.push(Router::with_path("{id}").get(get_user))
);
let acceptor = TcpListener::new("0.0.0.0:8080").bind().await;
Server::new(acceptor).serve(router).await;
}
JsonBody<T> for simple JSON extractionExtractible for complex multi-source extractionreq.param::<i64>)