引言
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 | ... |
接下来,我们在 main.rs
中编写一个最简单的 API,负责在访问对应路由时打印出 Hello, Rocket
。可以将下面的代码粘贴到你的文件中:
1 |
|
以上就完成了一个最简单的 API 编写。最后编译并启动 Web 服务后,可以在终端中看到下面的输出。你可以在浏览器中访问 localhost:8000
来查看响应内容:
1 | Configured for development. |
Rocket 的特性
在其官网的 Overview 中,可以看到 Rocket 框架的主要特性。
路由(Routing)
Rocket 的主要任务便是路由请求到合适的请求处理 Handler。路由是通过 get
属性声明的,看起来像是 Python 中的装饰器:
1 |
|
以上面的代码块为例,这个路由的名称为 index
,和 HTTP GET /
匹配。请求处理函数会返回一个字符串作为 HTTP 响应的 body。
动态参数(Dynamic Params)
Rocket 可以动态地识别请求路径多个部分,并和处理函数的参数一一匹配,示例如下:
1 |
|
上述示例表示,hello
路由会动态匹配到路径中的 <name>
和 <age>
部分。每个动态参数都要有具体的类型,Rocket 会尝试将 path
中对应位置的参数转换为目标类型。只有参数正确转换后,才会调用相应的路由处理函数。为了解析字符串,Rocket 会使用 FromParam
trait,你也可以为自定义类型实现该 trait。
请求数据处理(Handling Data)
请求的 body 数据是通过 FromData
trait 来处理的。请求的 body 数据可以派生任何实现了 FromData
的类型。你可以像下面这样指定期望通过哪个参数使用请求的 body:
1 |
|
Form
类型是 Rocket 内建的类型,它可以将 Web 表单转换成结构体。Rocket 会自动尝试将请求的 body 数据转换成 Form
,然后在转换成功后调用 login
处理函数。其它内建的 FormData
类型还有 Data
、Json
和 Flash
。
请求护卫(Request Guards)
除了动态路径和数据参数外,请求处理函数还可以包含第三种类型的参数:request guards。Request guards 不用在路由属性中声明,而是出现在请求处理函数的参数签名中,并且支持任意多个。
Request guards 的作用是对请求的元信息进行判断,如果满足指定条件,才运行处理函数,起到保护作用。比如:
1 |
|
例子中 ApiKey
用于保护 sensitive
处理函数,只有在请求头中的 API 密钥认证通过后,才会执行。ApiKey
类型需要过实现 FromRequest
trait,然后在实现逻辑中检查 API 密钥头。Request guards 是 Rocket 中非常强大且独特的概念。
响应(Responder)
请求处理函数的返回类型可以是任何实现了 Responder
的类型:1
2
fn route() -> T {}
上述例子中,T
必须要实现 Responder
。Rocket 已经为标准库中很多类型实现了 Responder
:&str
、String
、File
、Option
以及 Result
。Rocket 也实现了一些自定义的 Responder
,如 Redirect
、Flash
和 Template
。
Responder
的任务就是尽可能生成 Response
,它也可以在失败时提供状态码。如果指定了状态码,Rocket 就会调用相应的错误捕获函数。catch
路由的定义如下:
1 |
|
运行(Launching)
为了运行 Rocket 应用,首先需要挂载路由;然后才可以启动。示例如下:
1 | rocket::ignite() |
调用 launch
后会启动服务器,开发环境中,Rocket 会打印出一些比较有用的状态信息:
1 | 🚀 Rocket has launched from http://localhost:8000 |
Rocket 是如何工作的?
每一个 Rocket Web 应用从接受到请求到进行响应,都会经历标准的三个步骤(这也是常规 Web 框架都会做的事情)。
校验
首先,Rocket 会校验匹配的请求,确保处理函数中的每个类型都可以派生自当前请求。如果某个类型无法派生,则会定向到下一个匹配的路由,直到所有类型校验都没问题或者没有任何可尝试的路由为止。如果所有的路由都不匹配,则会返回自定义的 404 错误:
1 |
|
以上述例子来说明:
- 请求方法必须是
POST
- 请求路径必须是
/user
- 请求中必须包含
body
数据 - 请求的元信息必须是通过认证的
AdminUser
- 请求体必须能够转换成
User
结构体
处理
接下来,请求会被任意的 Handler 处理。这里就是执行具体的业务逻辑了。Rocket 中的 Handler 就是简单的函数,唯一要注意的是返回值必须要是实现了 Responder
trait 的类型。
响应
最后,Rocket 会将 Handler 返回值转换成 HTTP 响应,然后给客户端发送响应:
1 | fn route() -> T {} |
上述例子中的 T
必须是实现了 Responder
的类型,Rocket 目前提供了很多常用的响应类型:
Json<T>
: 将类型T
转换成 JSON 并返回Template
: 渲染模板并返回Redirect
: 返回合适的重定向响应NamedFile
: 向客户端流传输指定的文件,Content-Type
设置为文件夹的扩展名Stream
: 向客户端流传输任意Read
值
更多扩展功能
Rocket 框架可插拔的特性能能够给你带来很多额外的特性,这些特性在其文档中有详细的介绍。