diff --git a/docs/en/src/context.md b/docs/en/src/context.md index 67908955..1da88322 100644 --- a/docs/en/src/context.md +++ b/docs/en/src/context.md @@ -1,6 +1,6 @@ # Context -The main goal of `Context` is to acquire global data attached to Schema. **Note that if the return value of resolve function is borrowed from `Context`, you need to explictly state the lifetime of the argument.** +The main goal of `Context` is to acquire global data attached to Schema. **Note that if the return value of resolver function is borrowed from `Context`, you need to explictly state the lifetime of the argument.** The following example shows how to borrow data in `Context`. diff --git a/docs/en/src/cursor_connections.md b/docs/en/src/cursor_connections.md index ce68d142..157a3236 100644 --- a/docs/en/src/cursor_connections.md +++ b/docs/en/src/cursor_connections.md @@ -5,7 +5,7 @@ Relay's cursor connection specification is defined to provide a consistent metho It is simple to define a cursor connection in `Async-GraphQL` 1. Implement `async_graphql::DataSource` and write the `query_operation` function. -2. Call `DataSource::query` in the field's resolve function and return the result. +2. Call `DataSource::query` in the field's resolver function and return the result. Here is a simple data source that returns continuous integers: diff --git a/docs/en/src/define_complex_object.md b/docs/en/src/define_complex_object.md index 505b3eca..79274778 100644 --- a/docs/en/src/define_complex_object.md +++ b/docs/en/src/define_complex_object.md @@ -2,7 +2,7 @@ Different from `SimpleObject`, `Object` must have Resolve defined for each field in `impl`. -**A Resolve function has to be asynchronous. The first argument has to be `&self`, second being optional `Context` and followed by field arguments.** +**A resolver function has to be asynchronous. The first argument has to be `&self`, second being optional `Context` and followed by field arguments.** Resolve is used to get the value of the field. You can query a database and return the result. **The return type of the function is the type of the field.** You can also return a `async_graphql::FieldResult` so to return an error if it occrs and error message will be send to query result. diff --git a/docs/en/src/define_simple_object.md b/docs/en/src/define_simple_object.md index d1640a62..2299ef3e 100644 --- a/docs/en/src/define_simple_object.md +++ b/docs/en/src/define_simple_object.md @@ -1,6 +1,6 @@ # SimpleObject -`SimpleObject` directly map all field of a struct to GraphQL object, you cannot define a Resolve function on it. +`SimpleObject` directly map all field of a struct to GraphQL object, you cannot define a resolver function on it. The example below defined an object `MyObject`, including field `a` and `b`. `c` will be not mapped to GraphQL as it is labelled as `#[field(skip)]` diff --git a/docs/en/src/query_and_mutation.md b/docs/en/src/query_and_mutation.md index 251313fc..bdbc50e6 100644 --- a/docs/en/src/query_and_mutation.md +++ b/docs/en/src/query_and_mutation.md @@ -2,7 +2,7 @@ ## Query root object -The query root object is a GraphQL object with a definition similar to other objects. Resolve functions for all fields of the query object are executed concurrently. +The query root object is a GraphQL object with a definition similar to other objects. resolver functions for all fields of the query object are executed concurrently. ```rust diff --git a/docs/en/src/subscription.md b/docs/en/src/subscription.md index b7adb20a..ec0bbfdb 100644 --- a/docs/en/src/subscription.md +++ b/docs/en/src/subscription.md @@ -1,6 +1,6 @@ # Subscription -The definition of the subscription root object is slightly different from other root objects. Its Resolve function always returns a Stream, and the field parameters are usually used as data filtering conditions. +The definition of the subscription root object is slightly different from other root objects. Its resolver function always returns a Stream, and the field parameters are usually used as data filtering conditions. The following example subscribes to an integer stream, which generates one integer per second. The parameter step specifies the integer step size with a default of 1 diff --git a/docs/zh-CN/src/SUMMARY.md b/docs/zh-CN/src/SUMMARY.md index f6e31fdc..6cd2f1bb 100644 --- a/docs/zh-CN/src/SUMMARY.md +++ b/docs/zh-CN/src/SUMMARY.md @@ -18,6 +18,7 @@ - [输入值校验器](input_value_validators.md) - [查询缓存控制](cache_control.md) - [游标连接(Cursor Connections)](cursor_connections.md) + - [错误扩展](error_extensions.md) - [Apollo Tracing支持](apollo_tracing.md) - [集成到WebServer](integrations.md) - [Warp](integrations_to_warp.md) diff --git a/docs/zh-CN/src/cache_control.md b/docs/zh-CN/src/cache_control.md index c61f43b9..b5e8c85b 100644 --- a/docs/zh-CN/src/cache_control.md +++ b/docs/zh-CN/src/cache_control.md @@ -2,7 +2,7 @@ 生产环境下通常依赖缓存来提高性能。 -一个GraphQL查询会调用多个Resolve函数,每个Resolve函数都能够具有不同的缓存定义。有的可能缓存几秒钟,有的可能缓存几个小时,有的可能所有用户都相同,有的可能每个会话都不同。 +一个GraphQL查询会调用多个Resolver函数,每个Resolver函数都能够具有不同的缓存定义。有的可能缓存几秒钟,有的可能缓存几个小时,有的可能所有用户都相同,有的可能每个会话都不同。 `Async-graphql`提供一种机制允许定义结果的缓存时间和作用域。 diff --git a/docs/zh-CN/src/context.md b/docs/zh-CN/src/context.md index dbc86208..432aab12 100644 --- a/docs/zh-CN/src/context.md +++ b/docs/zh-CN/src/context.md @@ -1,6 +1,6 @@ # 查询上下文(Context) -查询上下文(Context)的主要作用是获取附加到Schema的全局数据,**需要注意的是,如果你的Resolve函数返回的数据借用了Context内保存的数据,需要明确指定生命周期参数**。 +查询上下文(Context)的主要作用是获取附加到Schema的全局数据,**需要注意的是,如果你的Resolver函数返回的数据借用了Context内保存的数据,需要明确指定生命周期参数**。 下面是一个返回值借用Context内数据的例子: diff --git a/docs/zh-CN/src/cursor_connections.md b/docs/zh-CN/src/cursor_connections.md index 3a3a984a..f703cc43 100644 --- a/docs/zh-CN/src/cursor_connections.md +++ b/docs/zh-CN/src/cursor_connections.md @@ -5,7 +5,7 @@ Relay定义了一套游标连接规范,以提供一致性的分页查询方式 在`Async-graphql`中定义一个连接非常简单,只需要两步: 1. 实现`async_graphql::DataSource`,重写`query_operation`函数。 -2. 在字段的Resolve函数中调用`DataSource::query`函数并传递响应参数。 +2. 在字段的Resolver函数中调用`DataSource::query`函数并传递响应参数。 下面是一个简单的获取连续整数的数据源: diff --git a/docs/zh-CN/src/define_complex_object.md b/docs/zh-CN/src/define_complex_object.md index b5d409df..c26cfcca 100644 --- a/docs/zh-CN/src/define_complex_object.md +++ b/docs/zh-CN/src/define_complex_object.md @@ -1,10 +1,10 @@ # 对象(Object) -和简单对象不同,对象必须为所有的字段定义Resolve函数,Resolve函数定义在impl块中。 +和简单对象不同,对象必须为所有的字段定义Resolver函数,Resolver函数定义在impl块中。 -**一个Resolve函数必须是异步的,它的第一个参数必须是`&self`,第二个参数是可选的`Context`,接下来是字段的参数。** +**一个Resolver函数必须是异步的,它的第一个参数必须是`&self`,第二个参数是可选的`Context`,接下来是字段的参数。** -Resolve函数用于计算字段的值,你可以执行一个数据库查询,并返回查询结果。**函数的返回值是字段的类型**,你也可以返回一个`async_graphql::FieldResult`类型,这样能够返回一个错误,这个错误信息将输出到查询结果中。 +Resolver函数用于计算字段的值,你可以执行一个数据库查询,并返回查询结果。**函数的返回值是字段的类型**,你也可以返回一个`async_graphql::FieldResult`类型,这样能够返回一个错误,这个错误信息将输出到查询结果中。 在查询数据库时,你可能需要一个数据库连接池对象,这个对象是个全局的,你可以在创建Schema的时候,用`SchemaBuilder::data`函数设置`Schema`数据, 用`Context::data`函数设置`Context`数据。下面的`value_from_db`字段展示了如何从`Context`中获取一个数据库连接。 diff --git a/docs/zh-CN/src/define_interface.md b/docs/zh-CN/src/define_interface.md index 2aa82852..15f71a13 100644 --- a/docs/zh-CN/src/define_interface.md +++ b/docs/zh-CN/src/define_interface.md @@ -1,6 +1,6 @@ # 接口(Interface) -接口用于抽象具有特定字段集合的对象,`Async-graphql`内部实现实际上是一个包装器,包装器转发接口上定义的Resolve函数到实现该接口的对象,所以接口类型所包含的字段类型,参数都必须和实现该接口的对象完全匹配。 +接口用于抽象具有特定字段集合的对象,`Async-graphql`内部实现实际上是一个包装器,包装器转发接口上定义的Resolver函数到实现该接口的对象,所以接口类型所包含的字段类型,参数都必须和实现该接口的对象完全匹配。 `Async-graphql`自动实现了对象到接口的转换,把一个对象类型转换为接口类型只需要调用`Into::into`。 diff --git a/docs/zh-CN/src/define_simple_object.md b/docs/zh-CN/src/define_simple_object.md index 5f6ee483..90906349 100644 --- a/docs/zh-CN/src/define_simple_object.md +++ b/docs/zh-CN/src/define_simple_object.md @@ -1,6 +1,6 @@ # 简单对象(SimpleObject) -简单对象是把Rust结构的所有字段都直接映射到GraphQL对象,不支持定义单独的Resolve函数。 +简单对象是把Rust结构的所有字段都直接映射到GraphQL对象,不支持定义单独的Resolver函数。 下面的例子定义了一个名称为MyObject的对象,包含字段`a`和`b`,`c`由于标记为`#[field(skip)]`,所以不会映射到GraphQL。 diff --git a/docs/zh-CN/src/error_extensions.md b/docs/zh-CN/src/error_extensions.md index 8edb259f..c304e382 100644 --- a/docs/zh-CN/src/error_extensions.md +++ b/docs/zh-CN/src/error_extensions.md @@ -1 +1,200 @@ -# 错误扩展 +# 错误扩展 + +引用 [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`中,所有面向用户的错误都强制转换为`FieldError`类型,默认情况下会提供 +由`std:::fmt::Display`暴露的错误消息。但是,`FieldError`实际上提供了一个额外的 +字段`Option`,如果它是一个`serde_json::Map`,则可以扩展错误的信息。 + +Resolver函数类似这样: + +```rust +async fn parse_with_extensions(&self) -> Result { + let my_extension = json!({ "details": "CAN_NOT_FETCH" }); + Err(FieldError("MyMessage", Some(my_extension))) + } +``` + +然后可以返回如下响应: + +```json +{ + "errors": [ + { + "message": "MyMessage", + "locations": [ ... ], + "path": [ ... ], + "extensions": { + "details": "CAN_NOT_FETCH", + } + } + ] +} +``` + + +## ErrorExtensions + +手动构造新的`FieldError`很麻烦。这就是为什么`Async-graphql`提供 +两个方便特性,可将您的错误转换为适当的`FieldError`扩展。 + +扩展任何错误的最简单方法是对错误调用`extend_with`。 +这将把任何错误转换为具有给定扩展信息的`FieldError`。 + +```rust +use std::num::ParseIntError; +async fn parse_with_extensions(&self) -> FieldResult { + Ok("234a" + .parse() + .map_err(|err: ParseIntError| err.extend_with(|_err| json!({"code": 404})))?) + } +``` + +### 为自定义错误实现ErrorExtensions + +你也可以给自己的错误类型实现`ErrorExtensions`: + + +```rust +#[macro_use] +extern crate thiserror; + +#[derive(Debug, 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) -> FieldError { + let extensions = match self { + MyError::NotFound => json!({"code": "NOT_FOUND"}), + MyError::ServerError(reason) => json!({ "reason": reason }), + MyError::ErrorWithoutExtensions => { + json!("This will be ignored since it does not represent an object.") + } + }; + + FieldError(format!("{}", self), Some(extensions)) + } +} +``` + +您只需要对错误调用`extend`即可将错误与其提供的扩展信息一起传递,或者通过`extend_with`进一步扩展错误信息。 + +```rust +async fn parse_with_extensions_result(&self) -> FieldResult { + // Err(MyError::NotFound.extend()) + // OR + Err(MyError::NotFound.extend_with(|_| json!({ "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 +use async_graphql::*; +async fn parse_with_extensions(&self) -> FieldResult { + Ok("234a" + .parse() + .extend_err(|_| json!({"code": 404}))?) + } + +``` + +### 链式调用 + +由于对所有`&E where E: std::fmt::Display`实现了`ErrorExtensions`和`ResultsExt`,我们可以将扩展链接在一起。 + +```rust +use async_graphql::*; +async fn parse_with_extensions(&self) -> FieldResult { + match "234a".parse() { + Ok(n) => Ok(n), + Err(e) => Err(e + .extend_with(|_| json!({"code": 404})) + .extend_with(|_| json!({"details": "some more info.."})) + // keys may also overwrite previous keys... + .extend_with(|_| json!({"code": 500}))), + } +} +``` + +响应: + +```json +{ + "errors": [ + { + "message": "MyMessage", + "locations": [ ... ], + "path": [ ... ], + "extensions": { + "details": "some more info...", + "code": 500, + } + } + ] +} +``` + +### 陷阱 + +Rust的稳定版本还未提供特化功能,这就是为什么`ErrorExtensions`为`&E where E: std::fmt::Display`实现,代替`E:std::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) -> FieldResult { + // the trait `error::ErrorExtensions` is not implemented + // for `std::num::ParseIntError` + "234a".parse().extend_err(|_| json!({"code": 404})) +} +``` + +但这可以通过编译: + +```rust,ignore,does_not_compile +async fn parse_with_extensions_result(&self) -> FieldResult { + // does work because ErrorExtensions is implemented for &ParseIntError + "234a" + .parse() + .map_err(|ref e: ParseIntError| e.extend_with(|_| json!({"code": 404}))) +} +``` + diff --git a/docs/zh-CN/src/error_handling.md b/docs/zh-CN/src/error_handling.md index d2e54022..0b0af3de 100644 --- a/docs/zh-CN/src/error_handling.md +++ b/docs/zh-CN/src/error_handling.md @@ -1,6 +1,6 @@ # 错误处理 -Resolve函数可以返回一个FieldResult类型,以下是FieldResult的定义: +Resolver函数可以返回一个FieldResult类型,以下是FieldResult的定义: ```rust type FieldResult = std::result::Result; diff --git a/docs/zh-CN/src/query_and_mutation.md b/docs/zh-CN/src/query_and_mutation.md index a73c0371..8933f98a 100644 --- a/docs/zh-CN/src/query_and_mutation.md +++ b/docs/zh-CN/src/query_and_mutation.md @@ -2,7 +2,7 @@ ## 查询根对象 -查询根对象是一个GraphQL对象,定义类似其它对象。查询对象的所有字段Resolve函数是并发执行的。 +查询根对象是一个GraphQL对象,定义类似其它对象。查询对象的所有字段Resolver函数是并发执行的。 ```rust use async_graphql::*; diff --git a/docs/zh-CN/src/subscription.md b/docs/zh-CN/src/subscription.md index 32393579..d8656747 100644 --- a/docs/zh-CN/src/subscription.md +++ b/docs/zh-CN/src/subscription.md @@ -1,6 +1,6 @@ # 订阅 -订阅根对象和其它根对象定义稍有不同,它的Resolve函数总是返回一个`Stream`,而字段参数通常作为数据的筛选条件。 +订阅根对象和其它根对象定义稍有不同,它的Resolver函数总是返回一个`Stream`,而字段参数通常作为数据的筛选条件。 下面的例子订阅一个整数流,它每秒产生一个整数,参数`step`指定了整数的步长,默认为1。