Rocket 框架基础

Rocket v0.5 DOC
Rocket是Rust的一个web框架,它使编写快速、安全的web应用程序变得简单,而不会牺牲灵活性、可用性或类型安全性。

  • 类型安全
    从请求到响应,Rocket确保您的类型有意义。
  • 样板免费
    把时间花在编写真正重要的代码上,让Rocket生成其余的代码。
  • 容易使用
    简单,直观的api使Rocket平易近人,无论你的背景如何。
  • 可扩展
    创建任何Rocket应用程序都可以使用的自己的一等(first-class)原语。

简介

Rocket是Rust的web框架。如果你愿意,你可以把Rocket想象成一个更灵活、更友好的RailsFlaskBottleYesod的混合体。我们更愿意把Rocket看作是一种新事物。Rocket的目标是快速、简单和灵活,同时尽可能提供有保证的安全。重要的是,Rocket还以有趣为目标,它通过确保您编写尽可能少的代码来完成任务来实现这一点。

本指南向您介绍了Rocket的核心,中级和高级概念。阅读本指南后,您应该会发现自己在使用Rocket时非常高效。

读者

假定读者已经很好地掌握了Rust编程语言。我们鼓励Rust新手阅读Rust Book。本指南还假设您对web应用程序的基础知识有基本的了解,例如路由和HTTP。Mozilla在其MDN web文档中对这些概念提供了很好的概述。

前言

Rocket的设计围绕着三个核心哲学:

  • 安全性、正确性和开发人员体验是最重要的。
    阻力最小的路径应该引导您获得最安全、最正确的web应用程序,尽管安全性和正确性不应该以降低开发人员体验为代价。Rocket易于使用,同时采取了大量措施来确保应用程序的安全性和正确性,而没有认知开销。

  • 所有请求处理信息都应该是输入的,并且是自包含的。
    因为web和HTTP本身是无类型的(或者有些人称之为字符串类型(stringly typed)),这意味着某些东西或某些人必须将字符串转换为本地类型。Rocket以零编程开销为您实现了这一点。更重要的是,Rocket的请求处理是自包含的(self-contained),没有全局状态: 处理程序是带有常规参数的常规函数

  • 不应该强迫做出决定。
    模板、序列化、会话以及其他几乎所有的东西都是可插拔的、可选的组件。虽然Rocket对这些功能都有官方支持和库,但它们是完全可选和可交换的。

这三个想法决定了Rocket的界面,你会发现它们都嵌入到了Rocket的核心功能中。

1、Quickstart

在开始编写Rocket应用程序之前,需要安装Rust工具链。我们建议使用rustup。如果您没有安装Rust,并且需要额外的指导,请参阅getting-started部分。

Running Examples

开始试验Rocket的绝对最快的方法是克隆Rocket存储库并运行examples/目录中包含的示例。例如,下面的一组命令运行hello示例:

git clone https://github.com/SergioBenitez/Rocket
cd Rocket
git checkout v0.5-rc
cd examples/hello
cargo run

在这里插入图片描述

examples/目录中有许多示例。它们都可以用cargo run

例子的Cargo.toml文件将指向本地克隆的rocket库。在复制示例供您自己使用时,您应该按入门指南中所述修改Cargo.toml

2、升级

与Rocket v0.4相比,Rocket v0.5带来了许多新特性和改进。Rocket v0.5还包含了许多改进,这些改进提高了框架和其中编写的应用程序的总体可用性、稳定性和安全性。虽然Rust编译器可以指导您完成许多这些更改,但其他更改需要特别注意。本指南的目的是指导您完成这些更改以及更多更改,将您的Rocket应用程序迁移到0.5,并从新特性和改进中获益。

本指南的目的不是取代CHANGELOG,而是补充。对于所有希望将其应用程序迁移到Rocket v0.5的开发人员来说,CHANGELOG应该被认为是必读的。

注意:不要惊慌!
简单地将Rocket的版本字符串升级到0.5系列将导致许多rustc编译器错误。但不要让这个困扰你!绝大多数的更改都是简单的重命名和#[async_trait]属性,它们会导致一连串的错误。因此,解决一个顶级问题(通常需要最小的、微不足道的更改)通常可以一次性解决许多错误。

2.1 Crate Organization

Rocket v0.5整合了改进的模块结构和板条箱生态系统。已移动或删除的模块和项将触发编译器错误。我们鼓励用户在CHANGELOG或API文档中搜索v0.5模拟。除了与异步I/O不兼容之外,所有以前存在的功能都在v0.5中可用。

2.2 默认关闭 Secrets

以前默认启用的private-cookies crate特性已重命名为secrets,默认禁用。如果您使用的是私有cookie,您必须在Cargo.toml中启用secrets功能:

[dependencies]
rocket = { version = "=0.5.0-rc.3", features = ["secrets"] }

2.3 Contrib 弃用

rocket_contrib crate已弃用,与Rocket 0.5完全不兼容。rocket_contrib的所有用户必须:

[dependencies]
- rocket = "0.4"
- rocket_contrib = { version = "0.4", features = ["json"], default-features = false }
+ rocket = { version = "=0.5.0-rc.3", features = ["json"] }
+ rocket_dyn_templates = { version = "=0.1.0-rc.3", features = ["tera"] }

注意:rocket_dyn_templates (and co.)不遵循rocket crate的版本锁定步骤。
这是有意为之。crate 依赖于许多外部依赖项,这些依赖项可能以不同于Rocket本身的速度发展。允许它们的版本分离,可以在不破坏Rocket本身的情况下保持依赖关系的最新。

以前在rocket_contrib中的所有功能都可用。有关详细信息,请参阅CHANGELOG的贡献毕业部分

2.4 Stable and Async Support

Rocket v0.5在Rust stable上编译和构建完全异步核心。我们鼓励你:

  • 为生产构建切换到Rust稳定发布通道。
  • 删除之前需要的#![feature(..)] crate属性。

所有申请作者必须(must):

  • 使用rocket::build()代替rocket::ignite()
  • 使用#[launch]#[rocket::main]异步入口属性。
  • 使用任何阻塞I/O的async版本或在另一个线程中执行它。
    应用程序作者可以(may):
  • 更倾向于通过use而不是#[macro_use]显式导入宏。

本节的其余部分将详细描述如何进行这些更改。

2.5 Stable Release Channel

如果你喜欢使用Rust的稳定发布通道,你可以使用rustup切换到它:

## switch globally
rustup default stable

## switch locally
rustup override set stable

使用稳定发布通道可确保在升级Rust编译器或Rocket时不会发生破坏。话虽如此,Rocket继续利用只有夜间频道才有的功能。因此,在可预见的未来,夜间的开发体验将更加优越。例如,nightly编译器的诊断更详细和准确:

我们的建议是在夜间通道上进行本地开发,而在稳定通道上构建和部署生产。

2.6 Feature Attribute

作为对稳定发布通道的支持,Rocket应用程序不再需要启用任何要使用的特性。你应该删除任何#[feature(..)]crate属性:

- #![feature(proc_macro_hygiene, decl_macro)]
-
  #[macro_use] extern crate rocket;

  fn main() { .. }

3、入门指南(Getting Started)

让我们创建并运行第一个Rocket应用程序。我们将确保安装了一个兼容的Rust工具链,创建一个依赖于Rocket的新Cargo项目,然后运行应用程序。

3.1 安装 Rust

Rocket使用了最新的Rust特性。因此,我们需要最新的Rust版本来运行Rocket应用程序。如果您已经安装了最新的Rust编译器,可以直接跳到下一节。

要安装最新版本的Rust,我们建议使用rustup。按照其网站上的说明安装rustup。安装rustup后,通过运行命令确保安装了最新的工具链

rustup default stable

注意:您可能更喜欢使用 nightly 通道进行开发。
使用Rocket开发时,每晚的Rust工具链可以改善某些开发人员体验,例如更好的编译时诊断。您可以选择在夜间频道上进行开发,以利用这些改进的体验。请注意,所有的Rocket特性都可以在所有Rust通道中使用。
要将nightly工具链设置为默认值,请运行rusetup default nightly

3.2 Hello, world!

让我们编写第一个Rocket应用程序!首先创建一个新的基于二进制文件的Cargo项目,并切换到新目录:

cargo new hello-rocket --bin
cd hello-rocket

现在,将Rocket作为依赖项添加到Cargo.toml中:

[dependencies]
rocket = "=0.5.0-rc.3"

警告:开发版本必须是git依赖项。
带有-dev标记的开发版本没有发布。要依赖于Rocket的开发版本,您需要指向Cargo.toml到Rocket git存储库。例如,将######替换为git提交散列:
[dependencies] rocket = { git = "https://github.com/SergioBenitez/Rocket", rev = "######" }

修改src/main.rs。这样它就包含了Rocket Hello, world!应用程序,如下:

#[macro_use] extern crate rocket;

#[get("/")]
fn index() -> &'static str {
    "Hello, world!"
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/", routes![index])
}

我们现在不会详细解释这个程序的功能;我们把这个留到指南的其余部分。简而言之,它创建一个index路由,在/路径上挂载该路由,然后启动应用程序。用cargo run编译并运行程序。您应该看到以下内容:

> cargo run
🔧 Configured for debug.
   >> address: 127.0.0.1
   >> port: 8000
   >> workers: [..]
   >> keep-alive: 5s
   >> limits: [..]
   >> tls: disabled
   >> temp dir: /tmp
   >> log level: normal
   >> cli colors: true
🛰  Routes:
   >> (index) GET /
🚀 Rocket has launched from http://127.0.0.1:8000

在这里插入图片描述

访问http://localhost:8000查看您的第一个实际使用的Rocket应用程序!

小贴士:不喜欢颜色或表情符号?
在运行Rocket二进制文件时,可以通过将ROCKET_CLI_COLORS环境变量设置为0false来禁用颜色和表情符号:ROCKET_CLI_COLORS=false cargo run

4、概述

Rocket提供了用Rust构建web服务器和应用程序的原语:Rocket提供了路由、请求预处理和响应后处理;剩下的就看你了。您的应用程序代码指示Rocket对什么进行预处理和后处理,并填补了预处理和后处理之间的空白。

4.1 生命周期

Rocket的主要任务是监听传入的web请求,将请求分派给应用程序代码,并向客户端返回响应。我们把从请求到响应的过程称为“生命周期”。我们将生命周期总结为以下步骤序列:

4.1.1 Routing

Rocket将传入的HTTP请求解析为代码间接操作的本地结构。Rocket通过匹配应用程序中声明的路由属性来确定调用哪个请求处理程序。

4.1.2 Validation

Rocket根据匹配路由中存在的类型和守卫(types and guards)验证传入请求。如果验证失败,Rocket将请求转发到下一个匹配路由或调用错误处理程序。

4.1.3 Processing

使用经过验证的参数调用与路由关联的请求处理程序。这是应用程序的主要业务逻辑。处理通过返回一个Response完成。

4.1.4 Response

处理返回的Response。Rocket生成适当的HTTP响应并将其发送到客户端。这就完成了生命周期。Rocket继续侦听请求,重新启动每个传入请求的生命周期。

本节的其余部分将详细介绍路由阶段以及Rocket开始将请求分派给请求处理程序所需的其他组件。下面的部分描述了请求和响应阶段以及Rocket的其他组件。

4.2 Routing

Rocket 应用程序以路由(routes)和处理程序(handlers)为中心。路由是以下内容的组合:

  • 用来匹配传入请求的一组参数
  • 处理请求并返回响应的处理程序

处理程序只是一个函数,它接受任意数量的参数并返回任意类型。

要匹配的参数包括静态路径、动态路径、路径段、表单、查询字符串、请求格式说明符和主体数据。Rocket使用的属性,看起来就像其他语言中的函数装饰器,使声明路由变得容易。路由是通过在函数(handler)上加上要匹配的一组参数来声明的。完整的路由声明是这样的:

#[get("/world")]              // <- route attribute
fn world() -> &'static str {  // <- request handler
    "hello, world!"
}

这声明了与传入GET请求的静态路径"/world"相匹配的world路由。我们可以使用#[post]#[put]来代替#[get],或者使用#[catch]来提供自定义错误页面。此外,在构建更有趣的应用程序时,可能需要其他路由参数。接下来的请求一章,有关于路由和错误处理的更多细节。

注意:我们更喜欢#[macro_use],但您可能更喜欢显式导入。
在本指南和Rocket的大部分文档中,我们使用#[macro_use]显式导入rocket,尽管Rust 2018版本将显式导入crate变为可选选项。然而,使用#[macro_use]全局显式导入宏,允许您在应用程序的任何地方使用Rocket的宏,而无需显式导入它们。
相反,您可能更喜欢显式导入宏或使用绝对路径引用它们:use rocket::get; or #[rocket::get]

4.3 Mounting

在Rocket向路由发送请求之前,需要挂载路由:

rocket::build().mount("/hello", routes![world]);

mount方法的输入是:

  1. 命名空间的基本路径,其下有一系列路由,这里是/hello
  2. 通过routes!声明的一系列路由:在这里是routes![world],有多个路由routes![a, b, c]

这将通过build函数创建一个新的Rocket实例,并将world路由挂载到/hello基本路径,使Rocket知道该路由。对/hello/worldGET请求将被定向到world函数。

mount方法,像Rocket上的所有其他构建器方法一样,可以被链接任意多数,并且路由可以被挂载点重用:

rocket::build()
    .mount("/hello", routes![world])
    .mount("/hi", routes![world]);

通过将world挂载到/hello/hi,对"/hello/world""/hi/world"的请求将被定向到world函数。

在许多情况下,基本路径只是"/"

4.4 Launching

Rocket在启动(launched)后开始服务请求,它启动一个多线程异步服务器,并在请求到达时将其分发到匹配的路由。

Rocket启动有两种机制。第一种也是首选的方法是通过#[launch] route属性,它生成一个main函数来设置异步运行时并启动服务器。有了#[launch],我们完整的Hello, world!应用程序看起来像:

#[macro_use] extern crate rocket;

#[get("/world")]
fn world() -> &'static str {
    "Hello, world!"
}

#[launch]
fn rocket() -> _ {
    rocket::build().mount("/hello", routes![world])
}

提示:#[launch]推断返回类型!
对于Rocket的#[launch]属性来说,当返回类型设置为_时,用#[launch]装饰的函数的返回类型会自动推断出来。如果愿意,还可以显式地将返回类型设置为Rocket<Build>

如果我们访问http://127.0.0.1:8000/hello/world,我们看到Hello, world!正如我们所预料的那样。

注意:这个和其他的例子都在GitHub上。
可以在GitHub上找到这个示例的完整crate的扩展版本,该版本已准备好cargo run。您可以在GitHub示例目录中找到数十个其他完整的示例,涵盖了Rocket的所有功能。

第二种方法使用#[rocket::main]路由属性。#[rocket::main]也生成了一个main函数来设置异步运行时,但与#[launch]不同的是,它允许你启动服务器:

#[rocket::main]
async fn main() -> Result<(), rocket::Error> {
    let _rocket = rocket::build()
        .mount("/hello", routes![world])
        .launch()
        .await?;

    Ok(())
}

#[rocket::main]在需要使用launch()返回的Future或要检查launch()的返回值时非常有用。例如,错误处理示例检查返回值。

4.5 Futures and Async

Rocket使用Rust Futures实现并发。使用Futures 和async/await的异步编程允许路由处理程序执行等待繁重的I/O,如文件系统和网络访问,同时仍然允许其他请求取得进展。有关Rust Futures的概述,请参阅Rust中的异步编程

一般来说,您应该更喜欢在Rocket应用程序中使用async-ready库,而不是同步库。

async出现在Rocket的几个地方:

  • 路由错误捕获器可以是异步的。在async fn函数中,你可以.await来自Rocket或其他库的Futures。
  • Rocket的一些特性,比如FromDataFromRequest,都有返回Futures的方法。
  • DataDataStream(传入请求数据)以及ResponseBody(传出响应数据)基于tokio::io::AsyncRead,而不是std::io::Read

您可以用async标签在crates.io上找到async-ready

Rocket v0.5使用tokio运行时。如果您使用#[launch]#[rocket::main],运行时将为您启动,但是您仍然可以通过不使用任何属性在自定义构建的运行时上launch()一个rocket实例。

4.5.1 Async Routes

Rocket使得在路由中使用async/await变得很容易。

use rocket::tokio::time::{sleep, Duration};

#[get("/delay/<seconds>")]
async fn delay(seconds: u64) -> String {
    sleep(Duration::from_secs(seconds)).await;
    format!("Waited for {} seconds", seconds)
}

首先,注意路由函数是一个async fn。这允许在处理程序中使用awaitsleep是一个异步函数,所以我们必须await它。

4.5.1 多任务(Multitasking)

Rust的Futures是一种合作的多任务处理形式。一般来说,Futures和async fns应该只对操作进行.await,而不会阻塞。阻塞的一些常见示例包括锁定非异步互斥锁、连接线程或使用执行I/O的非异步库函数(包括std中的函数)。

如果Futureasync fn阻塞了线程,就会出现低效的资源使用、停滞,有时甚至会出现死锁。

有时,库或操作没有好的异步替代方案。如果需要,可以使用tokio::task::spawn_blocking将同步操作转换为异步操作:

use std::io;

use rocket::tokio::task::spawn_blocking;

#[get("/blocking_task")]
async fn blocking_task() -> io::Result<Vec<u8>> {
    // In a real app, use rocket::fs::NamedFile or tokio::fs::File.
    let vec = spawn_blocking(|| std::fs::read("data.txt")).await
        .map_err(|e| io::Error::new(io::ErrorKind::Interrupted, e))??;

    Ok(vec)
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值