async-graphql/docs/zh-CN/src/error_extensions.md
Edward Rudd 3b7ed74d11 correct doc examples so they compile
- examples to fix still
  - error_extensions.md ResultExt example does not compile!
     - trait ErrorExtensions is not implemented for ParseIntError
  - dataloader
     - requires sqlx to work. So we either "stub" it OR we rewrite them simpler to use a  simple "faux" db library
2022-06-02 17:32:12 -04:00

235 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

错误扩展
引用 [graphql-spec](https://spec.graphql.org/June2018/#example-fce18)
> GraphQL服务可以通过扩展提供错误的附加条目。
> 该条目(如果设置)必须是一个映射作为其值,用于附加错误的其它信息。
## 示例
我建议您查看此 [错误扩展示例](https://github.com/async-graphql/examples/blob/master/actix-web/error-extensions/src/main.rs) 作为快速入门。
## 一般概念
在`Async-graphql`中,所有面向用户的错误都强制转换为`Error`类型,默认情况下会提供
由`std:::fmt::Display`暴露的错误消息。但是,`Error`实际上提供了一个额外的可以扩展错误的信息。
Resolver函数类似这样
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32, Error> {
Err(Error::new("MyMessage").extend_with(|_, e| e.set("details", "CAN_NOT_FETCH")))
}
# }
```
然后可以返回如下响应:
```json
{
"errors": [
{
"message": "MyMessage",
"locations": [ ... ],
"path": [ ... ],
"extensions": {
"details": "CAN_NOT_FETCH",
}
}
]
}
```
## ErrorExtensions
手动构造新的`Error`很麻烦。这就是为什么`Async-graphql`提供
两个方便特性,可将您的错误转换为适当的`Error`扩展。
扩展任何错误的最简单方法是对错误调用`extend_with`。
这将把任何错误转换为具有给定扩展信息的`Error`。
```rust
# extern crate async_graphql;
# use async_graphql::*;
# struct Query;
use std::num::ParseIntError;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.map_err(|err: ParseIntError| err.extend_with(|_err, e| e.set("code", 404)))?)
}
# }
```
### 为自定义错误实现ErrorExtensions
你也可以给自己的错误类型实现`ErrorExtensions`:
```rust
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[error("Could not find resource")]
NotFound,
#[error("ServerError")]
ServerError(String),
#[error("No Extensions")]
ErrorWithoutExtensions,
}
impl ErrorExtensions for MyError {
// lets define our base extensions
fn extend(&self) -> Error {
Error::new(format!("{}", self)).extend_with(|err, e|
match self {
MyError::NotFound => e.set("code", "NOT_FOUND"),
MyError::ServerError(reason) => e.set("reason", reason.clone()),
MyError::ErrorWithoutExtensions => {}
})
}
}
```
您只需要对错误调用`extend`即可将错误与其提供的扩展信息一起传递,或者通过`extend_with`进一步扩展错误信息。
```rust
# extern crate async_graphql;
# extern crate thiserror;
# use async_graphql::*;
# #[derive(Debug, thiserror::Error)]
# pub enum MyError {
# #[error("Could not find resource")]
# NotFound,
#
# #[error("ServerError")]
# ServerError(String),
#
# #[error("No Extensions")]
# ErrorWithoutExtensions,
# }
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions_result(&self) -> Result<i32> {
// Err(MyError::NotFound.extend())
// OR
Err(MyError::NotFound.extend_with(|_, e| e.set("on_the_fly", "some_more_info")))
}
# }
```
```json
{
"errors": [
{
"message": "NotFound",
"locations": [ ... ],
"path": [ ... ],
"extensions": {
"code": "NOT_FOUND",
"on_the_fly": "some_more_info"
}
}
]
}
```
## ResultExt
这个特质使您可以直接在结果上调用`extend_err`。因此上面的代码不再那么冗长。
```rust,ignore
# // @todo figure out why this example does not compile!
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
Ok("234a"
.parse()
.extend_err(|_, e| e.set("code", 404))?)
}
# }
```
### 链式调用
由于对所有`&E where E: std::fmt::Display`实现了`ErrorExtensions`和`ResultsExt`,我们可以将扩展链接在一起。
```rust
# extern crate async_graphql;
use async_graphql::*;
# struct Query;
# #[Object]
# impl Query {
async fn parse_with_extensions(&self) -> Result<i32> {
match "234a".parse() {
Ok(n) => Ok(n),
Err(e) => Err(e
.extend_with(|_, e| e.set("code", 404))
.extend_with(|_, e| e.set("details", "some more info.."))
// keys may also overwrite previous keys...
.extend_with(|_, e| e.set("code", 500))),
}
}
# }
```
响应:
```json
{
"errors": [
{
"message": "MyMessage",
"locations": [ ... ],
"path": [ ... ],
"extensions": {
"details": "some more info...",
"code": 500,
}
}
]
}
```
### 缺陷
Rust的稳定版本还未提供特化功能这就是为什么`ErrorExtensions`为`&E where E: std::fmt::Display`实现,代替`Estd::fmt::Display`通过提供一些特化功能。
[Autoref-based stable specialization](https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md).
缺点是下面的代码**不能**编译:
```rust,ignore,does_not_compile
async fn parse_with_extensions_result(&self) -> Result<i32> {
// the trait `error::ErrorExtensions` is not implemented
// for `std::num::ParseIntError`
"234a".parse().extend_err(|_, e| e.set("code", 404))
}
```
但这可以通过编译:
```rust,ignore,does_not_compile
async fn parse_with_extensions_result(&self) -> Result<i32> {
// does work because ErrorExtensions is implemented for &ParseIntError
"234a"
.parse()
.map_err(|ref e: ParseIntError| e.extend_with(|_, e| e.set("code", 404)))
}
```