6 个版本

0.7.1 2024年3月19日
0.7.0 2024年3月19日
0.6.3 2024年2月16日

#1335网页开发

Download history 10/week @ 2024-04-01 3/week @ 2024-05-13 15/week @ 2024-05-20 7/week @ 2024-06-03 12/week @ 2024-06-10

每月 343 次下载

MIT 许可证

67KB
1.5K SLoC

Resp Result

网页框架响应的帮助数据结构

Github Crates.io Licente

为什么

  • ResultErr(_) 作为网页框架的响应类型时,会变成 500,这不是我期望的行为
  • 使用非 Result 类型作为网页框架的响应类型时,不能使用 ?,代码将充满 if letmatch

这就是为什么我需要一个 RespResult 的原因,它可以

  • 当它变成 RespResult::Err 时,控制响应代码或其他消息,而不总是 500
  • 实现 Try,因此可以使用友好的 ? 来简化代码

注意:因为 Try 还不稳定,这个 crate 需要 Nightly Rust

用法

安装

resp-result 添加到您的 crate

[dependencies]
resp-result = "*"

功能标志

  • extra-error:在 RespError 特性中启用额外错误消息
  • log:将 tracing 也作为日志记录到 log
  • tracing:启用使用 tracing 的记录器
  • nightly_try_v2:为 RespResult 实现 Try,使其可以使用 ?,它将启用功能 try_trait_v2 并需要 Nightly Rust 编译器

定义一个错误

RespResult<T,E> 需要 E 实现 RespError

例如

use resp_result::{RespError, RespResult};
use std::borrow::Cow;
use http::StatusCode;

pub struct PlainError(String);

impl RespError for PlainError{
    fn log_message(&self) -> Cow<'_, str> {
        Cow::Owned(format!("PlainError: {}", self.0))
    }

    fn resp_message(&self) -> Cow<'_, str> {
        "PlainError".into()
    }

    fn http_code(&self) -> StatusCode {
        StatusCode::BAD_REQUEST
    }

    type ExtraMessage = String;

    fn extra_message(&self) -> Self::ExtraMessage {
            self.0.clone()
    }
}
/// this can be used as a handler return type
type PlainRResult<T> = RespResult<T, PlainError>;

TRespResult<T, E> 中的界限

T 需要实现 Serialize 且具有 'static 生命周期

使用方法

以下是一个使用 RespResult 的示例

use resp_result::{RespError, RespResult};
use std::borrow::Cow;
use http::StatusCode;

pub struct PlainError(String);

impl RespError for PlainError{
    fn log_message(&self) -> Cow<'_, str> {
        Cow::Owned(format!("PlainError: {}", self.0))
    }

    type ExtraMessage = String;

    fn extra_message(&self) -> Self::ExtraMessage {
            self.0.clone()
    }
}
/// this can be used as a handler return type
type PlainRResult<T> = RespResult<T, PlainError>;

pub async fn welcome_short_name(name: String) -> PlainRResult<String>{
    if name.len() >= 8{
        // you can use `?` just like the function that returns `Result`
        Err(PlainError("the name size great then 8".to_string()))?;
    }

    if name.len() >= 4 {
        // `Result::Ok` can convert into `RespResult::Success` just using `into`
        Ok(format!("welcome! {name} with size great then 4")).into()
    }else{
        // or just direct using `RespResult::ok`
        RespResult::ok(format!("welcome! {name}"))
    }
}

ExtraFlag 和 ExtraFlags

通常,RespResult::Success 总是生成状态码为 200 OK 的响应,并使用 serde_json 将体序列化为 JSON。但有时我们想返回一个 304 Not Modified,以空体告知客户端资源未更改。为了支持上述使用场景,出现了 ExtraFlagExtraFlags

Extra Flag

额外标志有 4 种不同类型,可以对响应产生不同的影响

  • empty_body:此标志将阻止 RespResult 将序列化到响应体中
  • status:此标志将覆盖响应的 StatusCode
  • set-header:此标志将提供的头插入或追加到响应头映射中
  • remove-header:此标志将从响应头映射中删除头

不同的额外标志可以使用 + 来组合效果,或者使用 += 来添加效果

Extra Flags

额外标志是一组额外标志

FlagWrap

标志包装提供发送额外标志的包装

当使用额外标志时,您需要将返回类型从 RespResult<T, E> 更改为 RespResult<FlagWrap<T>, E>

以下示例将状态码更改为 404 Not Found

use resp_result::{RespError, RespResult, FlagWrap, ExtraFlag};
use std::borrow::Cow;
use http::StatusCode;

pub struct PlainError(String);

impl RespError for PlainError{
    fn log_message(&self) -> Cow<'_, str> {
        Cow::Owned(format!("PlainError: {}", self.0))
    }

    type ExtraMessage = String;

    fn extra_message(&self) -> Self::ExtraMessage {
            self.0.clone()
    }
}
/// this can be used as the handler return type
type PlainRResult<T> = RespResult<T, PlainError>;

pub async fn welcome_short_name(
    name: String,
    ) -> PlainRResult<FlagWrap<String>>{
        if name.len() >= 8{
            RespResult::ok(
                format!("welcome! {name} your name size is {}",name.len()))
                // using `with_flags` to covert RespResult<T, E>
                // to `RespResult<FlagWrap<T>, E>`
                // using `()` for no extra flags
                .with_flags(())
        }else{
            // using `flag_ok` directly construct a flag with the resp result
            RespResult::flag_ok(
                format!("Welcome! {name}"),
                ExtraFlag::status(StatusCode::NOT_FOUND)
            )
        }
    }

影响 RespResult 的行为

默认情况下,RespResult 将像这样序列化响应体

{
  "is-ok": true,
  "error-message": "...",
  "extra-msg": "...",
  "body": null
}

默认行为可以通过使用 set_config 来设置全局配置来更改

例如,通过配置,我们可以将响应体更改为以下内容

{
  "status": "fail",
  "reterror": 10001,
  "message": "something wrong",
  "body": null
}

有关更多信息,请参阅 ConfigTrait 的文档

帮助宏

resp_result 属性宏

此宏用于函数。它将原始 Result<T,E> 转换为 RespResult,这使得编写处理程序更方便。

注意:要求 EResult 中实现 RespError

  • 示例
// the `rresult` is an alias of `resp_result`
// the function `test` now will return a `RespResult`
#[rresult]
fn test((a, b): (i32, i64), foo: String) -> Result<(), PlainError> {
    println!("{a},{b},{foo}");
    let a = foo.parse::<i32>()?;
    println!("{a:?}");
    Ok(())
}

RespError derive 宏

在实现 RespError 枚举时使用此宏,通常与 thiserror 一起使用

现在每个枚举的变体都有2个参数

  1. err_msg:返回给客户端的消息,通常需要与log_message相比删除敏感信息。如果没有提供,则使用log_message
  2. err_code:此类错误返回的Http状态码。如果没有提供,则默认为500

以下是一个示例

    use std::num::ParseIntError;

    use axum::extract::rejection::PathRejection;
    use axum_resp_result::RespError;
    #[derive(Debug, thiserror::Error, RespError)]
    pub(super) enum PlainError {
        #[error("Parse Request Path Error: {0}")]
        #[resp_result(
            err_msg = "Parse Request Path Error", 
            // the error_code can either the code number or the code name
            err_code = 400
        )]
        Path(#[from] PathRejection),
        #[error("Parse Int Error: {0}")]
        #[resp_result(err_msg = "Invalid Input Integer", err_code = "Bad Request")]
        ParseInt(#[from] ParseIntError),
    }

依赖项

~6-8MB
~146K SLoC