表单数据 #
表单是Web应用中收集用户输入的主要方式。Rocket提供了强大的表单处理功能,支持自动解析和验证。
FromForm Trait #
FromForm 是Rocket中用于表单数据解析的核心trait。
基本用法 #
rust
use rocket::form::Form;
use rocket::FromForm;
#[derive(FromForm)]
struct LoginForm {
username: String,
password: String,
}
#[post("/login", data = "<form>")]
fn login(form: Form<LoginForm>) -> String {
format!("Login: {} / {}", form.username, form.password)
}
表单提交 #
html
<form action="/login" method="POST">
<input type="text" name="username" />
<input type="password" name="password" />
<button type="submit">Login</button>
</form>
字段类型 #
基本类型 #
rust
#[derive(FromForm)]
struct BasicTypes {
text: String,
number: i32,
float: f64,
boolean: bool,
}
可选字段 #
rust
#[derive(FromForm)]
struct OptionalFields {
required: String,
optional: Option<String>,
number: Option<i32>,
}
默认值 #
rust
#[derive(FromForm)]
struct WithDefaults {
name: String,
#[field(default = 1)]
page: i32,
#[field(default = 10)]
per_page: i32,
#[field(default = "guest")]
role: String,
}
字段属性 #
重命名字段 #
rust
#[derive(FromForm)]
struct RenamedFields {
#[field(name = "user_name")]
username: String,
#[field(name = "user_email")]
email: String,
}
嵌套表单 #
rust
#[derive(FromForm)]
struct Address {
street: String,
city: String,
zip: String,
}
#[derive(FromForm)]
struct UserForm {
name: String,
email: String,
address: Address,
}
表单数据格式:
text
name=John&email=john@example.com&address.street=123+Main+St&address.city=NYC&address.zip=10001
数组字段 #
简单数组 #
rust
#[derive(FromForm)]
struct TagsForm {
#[field(name = "tag")]
tags: Vec<String>,
}
表单数据:
text
tag=rust&tag=web&tag=rocket
对象数组 #
rust
#[derive(FromForm)]
struct Item {
name: String,
quantity: i32,
}
#[derive(FromForm)]
struct OrderForm {
#[field(name = "items")]
items: Vec<Item>,
}
表单数据:
text
items[0].name=Apple&items[0].quantity=5&items[1].name=Orange&items[1].quantity=3
表单验证 #
内置验证 #
rust
use rocket::form::{self, FromForm, Result};
#[derive(FromForm)]
struct ValidatedForm {
#[field(validate = len(3..=20))]
username: String,
#[field(validate = len(8..))]
password: String,
#[field(validate = contains('@'))]
email: String,
#[field(validate = range(18..=120))]
age: u8,
}
自定义验证 #
rust
use rocket::form::{self, FromForm, Result};
#[derive(FromForm)]
struct CustomValidatedForm {
#[field(validate = validate_username)]
username: String,
#[field(validate = validate_password)]
password: String,
}
fn validate_username(username: &str) -> form::Result<'_, ()> {
if username.len() < 3 {
return Err(form::Error::validation("Username too short").into());
}
if username.len() > 20 {
return Err(form::Error::validation("Username too long").into());
}
if !username.chars().all(|c| c.is_alphanumeric() || c == '_') {
return Err(form::Error::validation("Invalid characters").into());
}
Ok(())
}
fn validate_password(password: &str) -> form::Result<'_, ()> {
if password.len() < 8 {
return Err(form::Error::validation("Password too short").into());
}
Ok(())
}
条件验证 #
rust
#[derive(FromForm)]
struct ConditionalForm {
account_type: String,
#[field(validate = validate_company_name(self.account_type.clone()))]
company_name: Option<String>,
}
fn validate_company_name(name: Option<&String>, account_type: String) -> form::Result<'_, ()> {
if account_type == "business" && name.is_none() {
return Err(form::Error::validation("Company name required for business accounts").into());
}
Ok(())
}
错误处理 #
捕获表单错误 #
rust
use rocket::form::Form;
use rocket::http::Status;
#[post("/submit", data = "<form>")]
fn submit(form: Result<Form<UserForm>, form::Error<'_>>) -> Result<String, Status> {
match form {
Ok(user) => Ok(format!("User: {}", user.name)),
Err(e) => {
eprintln!("Form error: {}", e);
Err(Status::BadRequest)
}
}
}
自定义错误消息 #
rust
use rocket::form::{self, FromForm, Contextual};
#[derive(FromForm)]
struct RegisterForm {
#[field(validate = len(3..=20).or_else(msg("Username must be 3-20 characters")))]
username: String,
#[field(validate = len(8..).or_else(msg("Password must be at least 8 characters")))]
password: String,
}
#[post("/register", data = "<form>")]
fn register(form: Form<Contextual<RegisterForm>>) -> String {
if let Some(ref err) = form.context {
format!("Error: {}", err)
} else {
format!("Registered: {}", form.username)
}
}
多部分表单 #
文件上传字段 #
rust
use rocket::form::Form;
use rocket::fs::TempFile;
#[derive(FromForm)]
struct UploadForm {
name: String,
file: TempFile<'static>,
}
#[post("/upload", data = "<form>")]
fn upload(mut form: Form<UploadForm>) -> String {
format!("Uploaded '{}' ({:?})", form.name, form.file.name())
}
多文件上传 #
rust
#[derive(FromForm)]
struct MultiUploadForm {
name: String,
files: Vec<TempFile<'static>>,
}
#[post("/upload-multi", data = "<form>")]
fn upload_multi(form: Form<MultiUploadForm>) -> String {
let count = form.files.len();
format!("Uploaded {} files for '{}'", count, form.name)
}
完整示例 #
rust
#[macro_use] extern crate rocket;
use rocket::form::{Form, FromForm, Contextual};
use rocket::fs::TempFile;
use rocket::response::Redirect;
#[derive(FromForm)]
struct UserRegistration {
#[field(validate = len(3..=20).or_else(msg("用户名需要3-20个字符")))]
username: String,
#[field(validate = len(8..).or_else(msg("密码至少8个字符")))]
password: String,
#[field(validate = contains('@').or_else(msg("邮箱格式不正确")))]
email: String,
#[field(default = 18)]
age: u8,
bio: Option<String>,
}
#[derive(FromForm)]
struct FileUpload {
description: String,
file: TempFile<'static>,
}
#[get("/register")]
fn register_form() -> &'static str {
r#"
<form action="/register" method="POST">
<input name="username" placeholder="Username" />
<input name="password" type="password" placeholder="Password" />
<input name="email" type="email" placeholder="Email" />
<input name="age" type="number" placeholder="Age" />
<textarea name="bio" placeholder="Bio"></textarea>
<button type="submit">Register</button>
</form>
"#
}
#[post("/register", data = "<form>")]
fn register(form: Form<Contextual<UserRegistration>>) -> String {
if let Some(ref context) = form.context {
let errors: Vec<_> = context.errors()
.map(|e| e.to_string())
.collect();
format!("Errors: {:?}", errors)
} else {
let user = form.into_inner();
format!("Registered: {} ({})", user.username, user.email)
}
}
#[post("/upload", data = "<form>")]
fn upload(mut form: Form<FileUpload>) -> String {
let filename = form.file.name().unwrap_or("unknown");
let size = form.file.len();
format!("Uploaded '{}' ({} bytes) - {}", filename, size, form.description)
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/", routes![register_form, register, upload])
}
测试示例 #
bash
# 注册用户
curl -X POST http://127.0.0.1:8000/register \
-d "username=alice" \
-d "password=password123" \
-d "email=alice@example.com" \
-d "age=25"
# 上传文件
curl -X POST http://127.0.0.1:8000/upload \
-F "description=My file" \
-F "file=@test.txt"
下一步 #
掌握了表单处理后,让我们继续学习 JSON处理,了解如何处理JSON请求数据。
最后更新:2026-03-28