用 Rust 构建 API 系列教程:第一部分
今天给大家带来一个系列:使用 Rust 构建 API。
在这个系列教程中,我将向你展示如何在 Rust 中构建一个简单的 API。由于我自己也是 Rust 的初学者,还有很多东西我需要学习。所以其中有不对的地方,欢迎批评指正。
这个系列一共分三部分。
第一部分将介绍基础知识,我将解释如何设置项目,并创建一个端点,返回一个 Hello World!信息。
在第二部分中,我将把 API 连接到 MongoDB,并添加两个端点来从数据库中创建和检索文档。
在第三部分也是最后一部分,我将向你展示如何构建身份验证中间件来保护其中一个端点。
Web 框架
我决定使用 tide[1] 作为 Web 框架。虽然比不上 actix[2] 流行(目前最流行的),但是我对 tide 更有感觉,在我看来它更简单。
项目设置
让我们开始设置这个项目。
首先,确保你已经安装了 Rust 和 Cargo[3]。
键入以下命令,使用 cargo 创建一个新项目:
cargo new rust-api-example-part-1
开始准备些代码,选择一个你最喜欢的编辑器开始。
如果你使用 VS Code,建议你安装以下扩展:
rust-analyzer,之前有介绍过,这个比官方的更好用; TOML Language Support,支持 TOML 文件格式语法高亮,支持 TOML 格式化,还计划支持 JSON 等格式和 TOML 转换;装这个插件,是因为 Cargo 的配置文件是这种格式。这也是我比较喜欢的格式,比太喜欢 YAML。
安装依赖项
在开始编码之前,我们需要安装依赖项。在这个阶段,我们只需要其中的三个:tide
, async-std
和 serde
。
正如我前面解释的,tide
是我们用来构建 API 的 Web 框架。
async-std
是异步运行时。默认情况下 Rust 不提供异步运行时,所以我们需要安装一个。目前有两种流行的选择:tokio
和 async-std
。
这有点烦人,因为这意味着你需要确保将在项目中使用的所有异步库都必须与你选择的异步运行时兼容。
tide
要求我们使用 async-std
,所以我们将安装这个运行时。
serde
是一个序列化和反序列化数据的框架(例如将 JSON 对象转换为 Rust 结构)。
打开 Cargo.toml 文件,并在 [dependencies] 下添加以下内容:
tide = "0.16"
async-std = { version = "1", features = ["attributes"] }
serde = { version = "1.0", features = ["derive"] }
你可以像我为 async-std
所做的那样指定额外的特性,这里,我们将需要 attributes
特性。
确保它能运行
默认情况下,在使用 cargo 创建项目时,应该有一个包含示例函数的默认 src/main.rs 文件。确保它能运行:
cargo run
第一次运行会比较慢。
在最后应该会打印:Hello,world!
接下来就是正式写代码的时候了。
开始编码
在 src/main.rs 文件中,添加如下代码:
#[async_std::main]
async fn main() -> tide::Result<()> {
let app = tide::new();
app.listen("127.0.0.1:8080").await?;
return Ok(());
}
main
现在是异步的,因为我们在前面添加了 async
关键字。为了能正确工作,我们需要添加一个运行时,这正是 #[async_std::main]
所做的。它是一个 "attribute" 宏,只是将我们的 main
函数包装在一个异步运行时中。
这个函数返回 tide::Result<()>
,这意味着它要么什么也不返回 (Ok(())
) ,要么返回一个 Tide 错误。
在函数内部,我们创建一个新的 Tide 实例,然后调用函数 listen
启动端口 8080
上的本地服务器。
如果你现在尝试访问 API,你会得到一个 404
,因为我们还没有定义任何路由。
让我们创建第一个控制器(controller),在 src/main.rs
中的 main
函数上面添加以下内容:
use tide::Request;
#[derive(Clone, Debug)]
struct State {}
async fn hello(_req: Request<State>) -> tide::Result {
return Ok("Hello world!".into());
}
hello
控制器是异步的,只有一个参数。因为我们实际上并没有使用这个参数,所以我们可以在每个约定中添加一个前导下划线。_req
参数的类型是 Request
(从 tide
导入) ,它需要一个 State
,这就是为什么我定义了一个空的 State
结构体。
State
结构体需要实现 Clone
特性(trait),因此我们使用从 serde
派生(derive
)的宏(marco)来实现它。
控制器返回类型得是 tide::Result
。
至于实现,我们只是返回 &str
类型值,并使用 .into()
函数将其转换为 tide::Result
。
这就是我们的控制器。现在我们需要对 main
函数做一些修改。改为下面的代码:
#[async_std::main]
async fn main() -> tide::Result<()> {
let mut app = tide::with_state(State {});
app.at("/hello").get(hello);
app.listen("127.0.0.1:8080").await?;
return Ok(());
}
我已经修改了我们应用程序的初始化,使其通过一个空 State
。稍后当我们需要传递数据库连接时,这将非常有用。
然后我添加了端点 GET /hello
。
现在,如果你运行这个应用程序,然后在浏览器访问 http://localhost:8080/hello
,你应该能看到 Hello world!
。
结尾
这就是该系列的第一部分,希望它对你有所帮助。
关于 Tide,可以访问仓库首页了解更多详细内容。
参考资料
tide: https://github.com/http-rs/tide
[2]actix: https://github.com/actix/actix
[3]Rust 和 Cargo: https://www.rust-lang.org/tools/install
推荐阅读
觉得不错,点个赞吧
扫码关注「Rust编程指北」