Rocket Web 框架入门

引言

Rocket 框架 是 Rust 生态中比较流行的 Web 框架之一,其最大的特点是拥有类似 Flask 那样比较简洁的写法,可以非常轻松地编写 RESTful API,同时还支持中间件等机制,易于扩展。当然,目前该框架最大的缺点是使用了很多 rust-nightly 版本中的特性,导致无法在 rust-stable 分支下编译。这对于一个需要长期维护的较大的项目来说,就存在一定的升级风险。不过鉴于目前我们不大可能在生产环境中使用 Rust 来编写 Web API,暂且还不用担心这些问题。

你好,Rocket

需要说明的是,下面的示例代码是在 rustc 1.34.0-nightly 环境下编译并运行的~

首先,需要新建项目 hello-rocket

1
cargo new hello-rocket --bin

由于需要使用 rust-nightly 版本,所以这里需要切换版本并更新工具链。方法如下:

1
2
# 将当前项目环境切到 nightly 版本
rustup override set nightly && rustup update

准备工作完成后,我们就需要给项目添加 rocket crate 依赖啦:

1
cargo add rocket

可以在 Cargo.toml 中看到添加的依赖:

1
2
3
...
[dependencies]
rocket = "0.4.0"

接下来,我们在 main.rs 中编写一个最简单的 API,负责在访问对应路由时打印出 Hello, Rocket。可以将下面的代码粘贴到你的文件中:

1
2
3
4
5
6
7
8
9
10
11
#![feature(proc_macro_hygiene, decl_macro)]
// 我们需要使用 rocket 中定义的路由创建宏 `routes!`
#[macro_use]
extern crate rocket;
#[get("/")]
fn index() -> String {
"Hello, Rocket".to_string()
}
fn main() {
rocket::ignite().mount("/", routes![index]).launch();
}

以上就完成了一个最简单的 API 编写。最后编译并启动 Web 服务后,可以在终端中看到下面的输出。你可以在浏览器中访问 localhost:8000 来查看响应内容:

1
2
3
4
5
6
7
8
9
10
11
12
Configured for development.
=> address: localhost
=> port: 8000
=> log: normal
=> workers: 8
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> tls: disabled
Mounting /:
=> GET / (index)
Rocket has launched from http://localhost:8000

Rocket 的特性

在其官网的 Overview 中,可以看到 Rocket 框架的主要特性。

路由(Routing)

Rocket 的主要任务便是路由请求到合适的请求处理 Handler。路由是通过 get 属性声明的,看起来像是 Python 中的装饰器:

1
2
3
4
#[get("/")]
fn index() -> &'static str {
"Hello, Rocket!"
}

以上面的代码块为例,这个路由的名称为 index,和 HTTP GET / 匹配。请求处理函数会返回一个字符串作为 HTTP 响应的 body。

动态参数(Dynamic Params)

Rocket 可以动态地识别请求路径多个部分,并和处理函数的参数一一匹配,示例如下:

1
2
3
4
#[get("/hello/<name>/<age>")]
fn hello(name: String, age: u8) -> String {
format!("Hello, {} year old, named {}!", age, name)
}

上述示例表示,hello 路由会动态匹配到路径中的 <name><age> 部分。每个动态参数都要有具体的类型,Rocket 会尝试将 path 中对应位置的参数转换为目标类型。只有参数正确转换后,才会调用相应的路由处理函数。为了解析字符串,Rocket 会使用 FromParam trait,你也可以为自定义类型实现该 trait。

请求数据处理(Handling Data)

请求的 body 数据是通过 FromData trait 来处理的。请求的 body 数据可以派生任何实现了 FromData 的类型。你可以像下面这样指定期望通过哪个参数使用请求的 body:

1
2
3
4
#[post("/login", data = "<user_form>")]
fn login(user_form: Form<UserLogin>) -> String {
format!("Hello, {}!", user_form.name)
}

Form 类型是 Rocket 内建的类型,它可以将 Web 表单转换成结构体。Rocket 会自动尝试将请求的 body 数据转换成 Form,然后在转换成功后调用 login 处理函数。其它内建的 FormData 类型还有 DataJsonFlash

请求护卫(Request Guards)

除了动态路径和数据参数外,请求处理函数还可以包含第三种类型的参数:request guardsRequest guards 不用在路由属性中声明,而是出现在请求处理函数的参数签名中,并且支持任意多个。

Request guards 的作用是对请求的元信息进行判断,如果满足指定条件,才运行处理函数,起到保护作用。比如:

1
2
3
4
#[get("/sensitive")]
fn sensitive(key: ApiKey) {

}

例子中 ApiKey 用于保护 sensitive 处理函数,只有在请求头中的 API 密钥认证通过后,才会执行。ApiKey 类型需要过实现 FromRequest trait,然后在实现逻辑中检查 API 密钥头。Request guards 是 Rocket 中非常强大且独特的概念。

响应(Responder)

请求处理函数的返回类型可以是任何实现了 Responder 的类型:

1
2
#[get("/")]
fn route() -> T {}

上述例子中,T 必须要实现 Responder。Rocket 已经为标准库中很多类型实现了 Responder&strStringFileOption 以及 Result。Rocket 也实现了一些自定义的 Responder,如 RedirectFlashTemplate

Responder 的任务就是尽可能生成 Response,它也可以在失败时提供状态码。如果指定了状态码,Rocket 就会调用相应的错误捕获函数。catch 路由的定义如下:

1
2
#[catch(404)]
fn not_found() -> T {}

运行(Launching)

为了运行 Rocket 应用,首先需要挂载路由;然后才可以启动。示例如下:

1
2
3
rocket::ignite()
.mount("/base", routes![index, another])
.launch();

调用 launch 后会启动服务器,开发环境中,Rocket 会打印出一些比较有用的状态信息:

1
🚀  Rocket has launched from http://localhost:8000

Rocket 是如何工作的?

每一个 Rocket Web 应用从接受到请求到进行响应,都会经历标准的三个步骤(这也是常规 Web 框架都会做的事情)。

校验

首先,Rocket 会校验匹配的请求,确保处理函数中的每个类型都可以派生自当前请求。如果某个类型无法派生,则会定向到下一个匹配的路由,直到所有类型校验都没问题或者没有任何可尝试的路由为止。如果所有的路由都不匹配,则会返回自定义的 404 错误:

1
2
3
4
#[post("/user", data = "<new_user>")]
fn new_user(admin: AdminUser, new_user: Form<User>) -> T {
...
}

以上述例子来说明:

  1. 请求方法必须是 POST
  2. 请求路径必须是 /user
  3. 请求中必须包含 body 数据
  4. 请求的元信息必须是通过认证的 AdminUser
  5. 请求体必须能够转换成 User 结构体

处理

接下来,请求会被任意的 Handler 处理。这里就是执行具体的业务逻辑了。Rocket 中的 Handler 就是简单的函数,唯一要注意的是返回值必须要是实现了 Responder trait 的类型。

响应

最后,Rocket 会将 Handler 返回值转换成 HTTP 响应,然后给客户端发送响应:

1
fn route() -> T {}

上述例子中的 T 必须是实现了 Responder 的类型,Rocket 目前提供了很多常用的响应类型:

  1. Json<T>: 将类型 T 转换成 JSON 并返回
  2. Template: 渲染模板并返回
  3. Redirect: 返回合适的重定向响应
  4. NamedFile: 向客户端流传输指定的文件,Content-Type 设置为文件夹的扩展名
  5. Stream: 向客户端流传输任意 Read

更多扩展功能

Rocket 框架可插拔的特性能能够给你带来很多额外的特性,这些特性在其文档中有详细的介绍。

  1. 模板
  2. Cookies
  3. 流传输
  4. 易于配置的开发环境
  5. 内建单元测试工具
  6. 类型安全的 URIs

参考

  1. Rocket Overview
0%