Rocket 0.5中GET请求查询参数优雅验证方案求助
Rocket 0.5 + rocket_okapi 0.8.0 GET查询参数优雅验证方案
核心思路:基于FromForm的字段级验证+自定义错误适配OpenAPI
针对你遇到的路由跳过、OpenAPI兼容冲突等问题,以下是可落地的解决方案:
1. 结构体字段级验证配置
直接在查询参数结构体上通过#[field(validate = ...)]完成基础校验,同时保留JsonSchema派生以支持OpenAPI文档生成:
use rocket::form::{FromForm, Validate}; use rocket_okapi::JsonSchema; #[derive(Debug, FromForm, JsonSchema, Validate)] pub struct HistoryRequest { #[field(validate = required)] #[field(validate = len(1..=100))] user_id: String, #[field(validate = required)] #[field(validate = range(1..=1000))] page: u32, #[field(validate = required)] #[field(validate = range(10..=50))] page_size: u32, }
通过required约束参数必须存在,配合长度、范围验证,直接在字段层面完成规则校验。
2. 自定义查询参数包装器适配错误与OpenAPI
由于直接使用Result<HistoryRequest, rocket::form::Error>会导致JsonSchema不兼容,我们可以实现一个通用包装器ValidatedQuery<T>,统一处理错误转换并实现JsonSchema:
use rocket::form::{FromForm, FormError}; use rocket_okapi::JsonSchema; use serde::Serialize; // 结构化错误返回体 #[derive(Debug, Serialize, JsonSchema)] pub struct QueryValidationError { field: String, message: String, } // 通用查询参数包装器 #[derive(JsonSchema)] pub struct ValidatedQuery<T>(pub T); impl<T: FromForm + JsonSchema> FromForm for ValidatedQuery<T> { type Error = Vec<QueryValidationError>; fn from_form(items: &mut rocket::form::Items<'_, '_>) -> rocket::form::Result<Self, Self::Error> { match T::from_form(items) { Ok(data) => Ok(ValidatedQuery(data)), Err(form_error) => { let errors = form_error.into_iter() .map(|e| QueryValidationError { field: e.name.unwrap_or("unknown".to_string()), message: e.to_string(), }) .collect(); Err(errors) } } } }
这个包装器会将FromForm的原生错误转换为可序列化的结构化数组,同时因为实现了JsonSchema,rocket_okapi可以正常生成对应的OpenAPI schema。
3. 路由层处理验证结果并返回响应
在路由中使用ValidatedQuery<HistoryRequest>捕获参数,错误时返回400 Bad Request与结构化错误:
use rocket::{get, http::Status, serde::json::Json}; use rocket_okapi::{openapi, openapi_get_routes}; #[openapi] #[get("/history?<query..>")] pub async fn get_history( query: Result<ValidatedQuery<HistoryRequest>, Vec<QueryValidationError>>, ) -> Result<Json<Vec<HistoryItem>>, (Status, Json<Vec<QueryValidationError>>)> { match query { Ok(ValidatedQuery(req)) => { // 执行业务逻辑并返回数据 let history = fetch_history(&req.user_id, req.page, req.page_size).await; Ok(Json(history)) } Err(errors) => Err((Status::BadRequest, Json(errors))), } } // 注册带OpenAPI文档的路由 pub fn routes() -> Vec<rocket::Route> { openapi_get_routes![get_history] }
该方案既保留了FromForm的验证能力,又能在参数缺失/无效时返回清晰的JSON错误,同时完全兼容OpenAPI文档生成。
4. 避免重复实现的技巧
ValidatedQuery<T>是通用包装器,所有需要验证的查询参数结构体只要实现FromForm和JsonSchema,就能直接套用,无需为每个结构体单独编写FromRequest逻辑。
内容的提问来源于stack exchange,提问作者FluffyDango
相关产品推荐
相关产品推荐

