6个版本
| 0.7.1 | 2024年3月19日 |
|---|---|
| 0.7.0 | 2024年3月19日 |
| 0.6.3 | 2024年2月16日 |
1532 in 过程宏
14KB
373 行
Resp Result
为网络框架响应提供帮助数据结构
为什么
Result当作为网络框架响应类型时,如果使用500作为错误代码,通常不是我所期望的- 使用非
Result类型作为网络框架响应类型时,不能使用?,代码将充满if let或match
这就是为什么我需要一个 RespResult,它可以
- 当它变成
RespResult::Err时,控制响应代码或其他消息,而不总是500 - 实现
Try,因此可以使用友好的?来简化代码
注意:因为
Try还不稳定,这个crate需要Nightlyrust
使用方法
安装
将 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,以告知客户端资源没有更改。为了支持上述使用情况,出现了 ExtraFlag 和 ExtraFlags
Extra Flag
extra flag 有 4 种不同的类型,可以对响应产生不同的影响
empty_body:此标志将阻止RespResult将序列化到响应体中status:此标志将覆盖响应的StatusCodeset-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 一起使用
现在枚举的每个变体都有两个参数
err_msg:返回给客户端的消息,通常需要与log_message相比删除敏感信息。如果没有提供,将使用log_messageerr_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