#response #web-framework #axum #macro #logging #error #resp-result

axum-resp-result-macro

为Axum响应提供帮助结构

6个版本

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

1532 in 过程宏


用于 axum-resp-result

MIT 许可证

14KB
373

Resp Result

为网络框架响应提供帮助数据结构

Github Crates.io Licente

为什么

  • Result 当作为网络框架响应类型时,如果使用 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 功能,并需要 夜间编译器

定义一个错误

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>;

RespResult<T, E>T 的边界

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

extra flag 有 4 种不同的类型,可以对响应产生不同的影响

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

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

Extra Flags

extra flags 是一组额外的标志

FlagWrap

flag wrap 提供了一个发送额外标志的包装器

当使用额外标志时,您需要将返回类型从 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 使用 set_config 来设置全局配置以更改默认行为

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

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

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

帮助宏

resp_result 属性宏

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

注意:在 Result 实现中需要 E,实现 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 派生宏

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

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

  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),
    }

依赖项

~1.2–1.7MB
~34K SLoC