要实现一个小的在线网页工具,最基本的功能就是能够返回一个 HTML form 表单, 让用户能填写数据,然后提交到 rust axum 后台,再将处理后的数据展示出来。
相对 PHP,Python,Go 的 web 框架,Rust 的 web 框架就晦涩很多,下面的示例如果换成其他语言, 在没有任何基础的情况下,估计一小时也能搞定,但是使用 rust axum 这个框架,我还是折腾了一晚上。
主要原因是:
- axum 的文档太简陋。如果不是 ChatGPT 帮助,我估计给我一天也搞不定。Google 也搜不到太多的资料。
- rust 语法晦涩,需要具备大量的基础知识
下面示例实现了一些基本的使用场景:
- 返回纯文本
- 返回 JSON
- 返回 HTML
- 返回 HTML form 表单
- 显示处理 form 提交数据后的页面
示例代码
use axum::{
routing::get,
Router,
response::{Json, Html},
Form,
};
use serde_json::{Value, json};
use serde::Deserialize;
#[tokio::main]
async fn main() {
// build our application with a single route
let app = Router::new()
.route("/", get(hello_text))
.route("/json", get(hello_json))
.route("/html", get(hello_html))
.route("/form", get(render_form).post(handle_form_submit))
;
println!("Serving on http://localhost:3000 ...");
axum::Server::bind(&"127.0.0.1:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
// `&'static str` becomes a `200 OK` with `content-type: text/plain; charset=utf-8`
async fn hello_text() -> &'static str {
"Hello, World!"
}
// `Json` gives a content-type of `application/json` and works with any type
// that implements `serde::Serialize`
async fn hello_json() -> Json<Value> {
Json(json!({ "domain": "www.sunzhongwei.com", "since": 1573 }))
}
// `Html` will get a `text/html` content-type
async fn hello_html() -> Html<&'static str> {
Html("
<h1>Hello HTML</h1>
<p>Hello, World!</p>
")
}
async fn render_form() -> Html<&'static str> {
Html(r#"
<html>
<head>
<title>Form Example</title>
</head>
<body>
<h1>Form Example</h1>
<form method="post">
<label for="field1">Field 1:</label>
<input type="text" name="field1" id="field1"><br>
<label for="field2">Field 2:</label>
<input type="text" name="field2" id="field2"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
"#)
}
#[derive(Deserialize)]
struct FormData {
field1: String,
field2: String,
}
async fn handle_form_submit(Form(form_data): Form<FormData>) -> Html<String> {
let response_html = format!(
r#"
<html>
<head>
<title>Form Submission Result</title>
</head>
<body>
<h1>Form Submission Result</h1>
<p>Field 1: {}</p>
<p>Field 2: {}</p>
</body>
</html>
"#,
form_data.field1, form_data.field2
);
Html(response_html)
}
为了简化,直接返回了 HTML 字符串,而没有使用正常框架内置的 HTML 模板。
可是 axum 也没有内置 HTML 模板功能,后续我将尝试三方模块库 askama, 非常类似 python jinja 的一个模板库。
编译运行
cargo run
然后在浏览器里输入
http://localhost:3000/form
即可看到 form 表单,并提交。如下图:
下面是遇到的一些问题,及相关知识点。
cannot find derive macro Deserialize
in this scope
error: cannot find derive macro `Deserialize` in this scope
--> src/main.rs:73:10
|
73 | #[derive(Deserialize)]
| ^^^^^^^^^^^
|
note: `Deserialize` is imported here, but it is only a trait, without a derive macro
问题出在我配置的 serde 依赖不对。有问题的 Cargo.toml 配置文件:
[dependencies]
axum = "0.6"
serde = "1.0.192"
serde_json = "1.0.108"
tokio = { version = "1.0", features = ["full"] }
其中的 serde 依赖,是我通过命令
cargo add serde
添加的。实际这是错误的,应该手动修改 Cargo.toml 文件,参考:
https://serde.rs/derive.html
正确的配置为:
serde = { version = "1.0", features = ["derive"] }
为何引入 serde 及 serde_json
在 Rust 中,标准库(std)没有提供内置的 JSON 序列化库。 可以使用第三方库 serde_json 来进行 JSON 的序列化和反序列化操作。 serde_json 是 Rust 社区中最常用的 JSON 序列化库之一, 它基于 serde 库提供了方便的 JSON 序列化和反序列化功能。
为何返回的 html 字符串前加上 r#
- r"some string" 是 rust 的原始字符串,及不识别任何转义。可以在里面随便写反斜杠和空白字符。
- r#"some string"# 则可以在字符串中使用双引号,而不需要转义。# 的数量可以为任意个,只要前后的 # 数量一致即可。
查看合集
📖 Rust web 框架 axum 教程:从入门到遥遥领先
参考
- https://docs.rs/axum/latest/axum/response/index.html
微信关注我哦 👍
我是来自山东烟台的一名开发者,有感兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式