1.2 异常处理

2022-10-09 • 更新于 2022-10-14

开始

首先不急着直接做各种其他的东西,而是将基础的东西弄好。

在 workspace 的 Cargo.toml 里添加 "ch01/error-handling",添加好后,文件的内容应该如下所示:

[workspace]

members = [
    "ch01/helloworld",
    "ch01/error-handling"
]

进入 ch01 文件夹,创建项目,名称为 error-handling。在项目的 Cargo.toml 里添加 actix-web 依赖,如下:

[dependencies]
actix-web = "4.2.1"

如果需要将自己的工作空间的所有代码上传到代码管理平台,需要将创建项目同时创建的 .git 文件夹删除。注意是项目的 .git 文件夹。

再在 main.rs 中,添加主函数,以及基础的内容:

use actix_web::{HttpServer, App};

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

上述的代码内容,之前基本都已经大致理解了一下。接下来开始异常处理的内容。

显示内容

刚开始暂时先不做其他的事情,而是显示一个简短的内容。看一下源码,源码中为函数 do_something 配置了路径和路由。那么我们一开始也就先做这一部分内容。

编写 do_something 函数,其中要注意到返回值的类型为 Result<HttpResponse, Error>。函数中返回一个字符串,内容为“Nothing interesting happened. Try again.”,意思大致是“没有什么有意思的事情发生,请再次尝试”。编写好的函数内容如下:

async fn do_something() -> Result<HttpResponse, Error> {
    Ok(HttpResponse::Ok().body("Nothing interesting happened. Try again."))
}

如果没有使用 rust-analyzer 的自动补充功能,此时顶部的引用应该已经如下所示:

> use actix_web::{HttpServer, App, HttpResponse, Error};
> ```
> 注意返回值中的 `Error`,是 `actix_web` 的,而不是 `std` 中的 `Error`。

接下来,要为其配置路径。查看官方例子中,发现此时用了另一种方式配置路由,即 `web::resource().route()`。

在顶部添加 `web` 引用。

rust use actix_web::web;


然后在 `App::new()` 后面,为 `do_something` 配置路径为 `/something`。代码如下:

rust async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new().service(web::resource(“/something”).route(web::get().to(do_something))) }) .bind((“127.0.0.1”, 8080))? .run() .await }


运行,进入网址 `localhost:8080/something` 看看,能显示 `do_something` 中返回的内容则说明代码没有问题。

## 添加错误
在源码中,自定义了一个名为 `CustomError` 的枚举类。在编写之前,先在 Cargo.toml 中添加 `derive_more`:

toml [dependencies] actix-web = “4.2.1” derive_more = “0.99.5”


搞定,现在添加 `CustomError`。内容如下:

rust use derive_more::Display;

#[derive(Debug, Display)] pub enum CustomError { #[display(fmt = “Custom Error 1”)] CustomOne, #[display(fmt = “Custom Error 2”)] CustomTwo, #[display(fmt = “Custom Error 3”)] CustomThree, #[display(fmt = “Custom Error 4”)] CustomFour, }


> 出现了新东西 `Display`。找到 `derive_more` 的[文档](https://jeltef.github.io/derive_more/derive_more/display.html),可以看到作者对 `Display` 的所有描述。按我的理解,大致作用是为自己声明的结构体等提供了一个格式化字符串的方式。

这个自定义的错误中,有四种错误。不一定有实际意义。接下来,继续查看源码,发现这些错误会随机展示一个出来。源码中,为 `rand::distributions::Standard` 实现了 `Distribution<CustomError>`,即为随机生成添加了 `CustomError` 这个类型的实现。通过这个实现可以在调用 `rand::random` 时,随机生成一个此类型的值。

在 Cargo.toml 中添加 `rand` 这个 crate 的依赖。

toml [dependencies] actix-web = “4.2.1” derive_more = “0.99.5” rand = “0.8”


然后在顶部添加 `Distribution` 和 `Standard` 的引用。

rust use rand::{prelude::Distribution, distributions::Standard};


现在,为 `Standard` 实现 `Distribution<CustomError>`。随机生成一个在[0,4)范围内的数字,然后根据生成的数字返回不同的 `CustomError`。

rust impl Distribution for Standard { fn sample(&self, rng: &mut R) -> CustomError { match rng.gen_range(0..4) { 0 => CustomError::CustomOne, 1 => CustomError::CustomTwo, 2 => CustomError::CustomThree, _ => CustomError::CustomFour, } } }


由于这个错误需要返回到网页上,所以需要实现 actix-web 的 `ResponseError`,来返回对应的 Http 回应。在官方例子的源码注释中有说明,返回 200 为正常回应,而返回的 Http 错误有4种:
1. 403 Forbidden(禁止访问)
2. 401 Unauthorized(缺少凭证)
3. 500 InternalServerError(内部服务器的错误)
4. 400 BadRequest(错误的请求)

> 可以通过[这个手册](https://cloud.tencent.com/developer/chapter/13553)来查看所有的 Http 错误。

自定义的四个错误,每一个枚举都对应上面4个错误中的一个。

rust use actix_web::{HttpServer, App, HttpResponse, web, Error, ResponseError};

impl ResponseError for CustomError { fn error_response(&self) -> HttpResponse { match self { CustomError::CustomOne => { println!(“do some stuff related to CustomOne error”); HttpResponse::Forbidden().finish()// 403 }, CustomError::CustomTwo => { println!(“do some stuff related to CustomOne error”); HttpResponse::Unauthorized().finish()// 401 }, CustomError::CustomThree => { println!(“do some stuff related to CustomOne error”); HttpResponse::InternalServerError().finish()// 500 }, _ => { println!(“do some stuff related to CustomOne error”); HttpResponse::BadRequest().finish()// 400 } } } }


## 随机产生错误
一般这些错误不会直接出现,所以需要随机触发一个。添加一个 `do_something_random` 的函数来随机触发一个错误。有 20% 的机率什么都不会发生。

rust use rand::{prelude::Distribution, distributions::Standard, thread_rng, Rng};

async fn do_something_random() -> Result<(), CustomError> { let mut rng = thread_rng();

if rng.gen_bool(2.0 / 10.0) {
    Ok(())
} else {
    Err(rand::random::<CustomError>())
}

}


然后在 `do_something` 中调用 `do_something_random`。

rust async fn do_something() -> Result { do_something_random().await?;

Ok(HttpResponse::Ok().body("Nothing interesting happened. Try again."))

} ```

现在来运行看看结果。多刷新几次可以看到浏览器展示不同的错误,控制台上也打印了相应的字符串。

结束

官方例子的注释说道,这个例子是用来展示如何自定义一个错误类型,来处理不同的错误。不过就目前而言确实有点抽象,可以知道的是这里的 Http 错误是由我们自己去产生的,而在 println 位置处,应该是需要之后去处理对应错误的函数。

当自定义一个错误类型的时候,为了能够使其返回给客户端/浏览器,需要实现 ResponseError 这个 traitResponseError 中定义的函数可以在文档中查看。

除此之外,还出现了新的路由配置,App::new().service(web::resource('').route())

actix-web学习笔记Rustactix-web学习笔记

学少何

不求上进的社畜……

1.3 状态

1.1 Hello world