salvo rust自定义异常处理

简介: salvo rust自定义异常处理

在项目的大多数业务处理过程中,除了要处理正常的业务逻辑,大多数情况要面对以下两种情况:

  • 不符合业务要求的请求,比如参数校验不通过,需要告知用户如何调整。
  • 服务器处理过程中产生错误,需要及时告知用户下一步应该如何处理。

面对以上情况,我们不可能要求参与项目的所有程序员都能灵活地处理,为了让开发人员把重心放在如何处理业务逻辑上,我们在项目搭建的时候,就要统一规范处理异常情况的方式。

示例

在salvo的使用过程中,提供了自定义异常的处理方式:

use salvo::prelude::*;
struct CustomError;
#[async_trait]
impl Writer for CustomError {
    async fn write(mut self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
        res.set_status_code(StatusCode::INTERNAL_SERVER_ERROR);
        res.render("custom error");
    }
}
#[handler]
async fn handle_anyhow() -> Result<(), anyhow::Error> {
    Err(anyhow::anyhow!("anyhow error"))
}
#[handler]
async fn handle_custom() -> Result<(), CustomError> {
    Err(CustomError)
}
#[tokio::main]
async fn main() {
    tracing_subscriber::fmt().init();
    let router = Router::new()
        .push(Router::with_path("anyhow").get(handle_anyhow))
        .push(Router::with_path("custom").get(handle_custom));
    tracing::info!("Listening on http://127.0.0.1:7878");
    Server::new(TcpListener::bind("127.0.0.1:7878")).serve(router).await;
}
复制代码

自定义error结构体

根据以上示例,我们可以来创建自己的自定义error:

首先,我们需要先创建一个结构体来承接至少3个字段:

  • code:便于前端判断响应结果是否正确。
  • msg:用于提示用户下一步应该如何处理
  • error:用于存放真实错误原因,主要是给开发人员看
use serde::Serialize;
// 先创建一个结构体来承接以下至少3个字段:
// code:便于前端判断响应结果是否正确
// msg:用于提示用户下一步应该如何处理
// error:用于存放真实错误原因,主要是给开发人员看
#[derive(Debug, PartialEq, Eq, Clone, Serialize)]
pub struct GlobalError {
    code: u16,
    // 提示信息
    msg: String,
    // 错误信息
    error: String,
}
复制代码

实例化error方法

其次,我们需要给出一些可以创建GlobalError实例的方法。

  • new函数是少不了的,在此基础上,我们可以补充一些更有针对性的方法,比如参数错误的时候,我们通过bad_request方法指定的error,就能够提高咱们的开发体验度。
  • 我们提供了将code自动转换为http status code的方式,让响应码优先使用http协议的响应码标准。
use salvo::{Depot, Request, Response, Writer, async_trait};
use salvo::http::StatusCode;
use salvo::prelude::Json;
impl GlobalError {
    pub fn new(code: u16, msg: &str, error: &str) -> GlobalError {
        GlobalError {
            code,
            msg: String::from(msg),
            error: String::from(error),
        }
    }
    pub fn bad_request(msg: &str, error: &str) -> GlobalError {
        GlobalError::new(StatusCode::BAD_REQUEST.as_u16(), msg, error)
    }
    // 将自定义error协议响应数据中
    pub fn write(self, res: &mut Response) {
        // 先把自定义code转换成http响应码
        let statusCode = StatusCode::from_u16(self.code);
        match statusCode {
            Err(_) => {
                // 转换失败的情况,说明这是自定义的code,那么http响应码就应该为200.
                res.set_status_code(StatusCode::OK);
            }
            Ok(code) => {
                res.set_status_code(code);
            }
        }
        // 写入响应数据
        res.render(Json(self));
    }
}
复制代码

实现Writer trait

最后,在我们大概实现了一些自定义实例化的方法后,我们接下来需要实现salvo的自定义error标准了,我们现在给自定义error实现salvo的Writer trait:

#[async_trait]
impl Writer for GlobalError {
    async fn write(self, _req: &mut Request, _depot: &mut Depot, res: &mut Response) {
        self.write(res);
    }
}
复制代码

真正的实现只需要调用我们前面写好的write方法。

如何使用

为了演示如何使用咱们的自定义error,我们编写了以下两个代码示例:

  • 示例1
#[handler]
async fn upload(req: &mut Request, res: &mut Response) {
  let file = req.file("file").await;
  // 如果从请求中拿到了文件
  if let Some(file) = file {
      match std::fs::copy(&file.path(), Path::new(&dest_file)) {
          // 如果文件存储成功,那么返回成功响应
          Ok(_) => {
                res.render(format!("id:{} \nfile_name:{}  \n文件上传成功:{}", id, file_name.name, &dest_file));
          }
          // 如果文件存储失败,那么告知用户,并且把真正的错误信息也要给出来,方便开发人员排查错误。
          // ps:真正的错误信息并不是要展示给用户看,前端可以选择不展示,或者把这个错误信息给到日志管理服务
          Err(e) => {
              GlobalError::new(500, "file store failure", e.to_string().as_str()).write(res);
          }
      }
    } else {
        // 如果请求中没有文件内容,直接创建error,并写入响应数据。
        GlobalError::bad_request("没有找到您要上传的文件,请重新上传!", "file not found in request").write(res);
    }
}
复制代码
  • 示例2
#[handler]
async fn create_account(req: &mut Request) -> Result<String, GlobalError> {
    let user_info: UserInfo = req.extract_body().await.unwrap();
    let account = &user_info.account;
    let password = &user_info.password;
    match AccountService::add_account(account, password) {
        // 如果账号添加成功,那么返回Ok
        Ok(x) => {
            info!("受影响的行数:{}", x);
            Ok(String::from("成功!"))
        }
        // 如果添加失败,直接返回自定义error
        Err(e) => Err(GlobalError::new(200, "创建账号失败", e.to_string().as_str()))
    }
}
复制代码
  • 通过以上示例,相信大部分小伙伴应该已经学会如何来使用自定义error了。

对于rust语言感兴趣的小伙伴欢迎一起来探讨学习,这个项目我已经把它上传到github上了,大家可以去上面下载hello_salvo,有问题也可以给我留言。


相关文章
|
3月前
|
Rust C语言
rust安装 -自定义安装路径和GCC安装
rust安装 -自定义安装路径和GCC安装
207 0
|
Rust C++ Windows
【Rust 实战】注册表之自定义Windows11任务栏位置(上)
【Rust 实战】注册表之自定义Windows11任务栏位置(上)
【Rust 实战】注册表之自定义Windows11任务栏位置(上)
|
JSON Rust Java
salvo搭建rust web项目
salvo搭建rust web项目
542 0
salvo搭建rust web项目
|
Rust Windows
【Rust 实战】注册表之自定义Windows11任务栏位置(下
【Rust 实战】注册表之自定义Windows11任务栏位置(下
|
Rust
salvo rust解析请求数据的各种姿势
salvo rust解析请求数据的各种姿势
284 0
|
2天前
|
Rust 安全 编译器
初探 Rust 语言与环境搭建
Rust 是一门始于2006年的系统编程语言,由Mozilla研究员Graydon Hoare发起,旨在确保内存安全而不牺牲性能。通过所有权、借用和生命周期机制,Rust避免了空指针和数据竞争等问题,简化了并发编程。相较于C/C++,Rust在编译时预防内存错误,提供类似C++的语法和更高的安全性。Rust适用于系统编程、WebAssembly、嵌入式系统和工具开发等领域。其生态系统包括Cargo包管理器和活跃社区。学习资源如&quot;The Book&quot;和&quot;Rust by Example&quot;帮助新手入门。安装Rust可通过Rustup进行,支持跨平台操作。
初探 Rust 语言与环境搭建
|
14天前
|
数据采集 Rust 监控
员工监控软件结合 Rust 语言的可能性
在企业管理精细化的趋势下,员工监控软件需求日益增长。Rust 语言以其安全性、高性能与并发能力,成为开发此类软件的理想选择。透过示例代码可见,无论是数据采集、处理或是网络通信,Rust 均能确保软件运行稳定、高效且避免常见的安全漏洞。随着 Rust 生态系统的持续发展,其为构建复杂监控系统提供了强大支持。这不仅提升了软件的整体表现,也更好地满足了企业对员工管理的需求。
17 1
|
13天前
|
Rust JavaScript Java
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
简单对比Java、Python、Go、Rust等常见语言计算斐波拉契数的性能
|
30天前
|
Web App开发 存储 Rust
日志存储问题之Dropbox也使用Rust语言如何解决
日志存储问题之Dropbox也使用Rust语言如何解决