Update docs

This commit is contained in:
sunli 2020-05-13 12:49:43 +08:00
parent fbfc1ff8b2
commit 0824b6cc23
17 changed files with 218 additions and 18 deletions

View File

@ -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`.

View File

@ -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:

View File

@ -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.

View File

@ -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)]`

View File

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

View File

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

View File

@ -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)

View File

@ -2,7 +2,7 @@
生产环境下通常依赖缓存来提高性能。
一个GraphQL查询会调用多个Resolve函数每个Resolve函数都能够具有不同的缓存定义。有的可能缓存几秒钟有的可能缓存几个小时有的可能所有用户都相同有的可能每个会话都不同。
一个GraphQL查询会调用多个Resolver函数每个Resolver函数都能够具有不同的缓存定义。有的可能缓存几秒钟,有的可能缓存几个小时,有的可能所有用户都相同,有的可能每个会话都不同。
`Async-graphql`提供一种机制允许定义结果的缓存时间和作用域。

View File

@ -1,6 +1,6 @@
# 查询上下文(Context)
查询上下文(Context)的主要作用是获取附加到Schema的全局数据**需要注意的是如果你的Resolve函数返回的数据借用了Context内保存的数据需要明确指定生命周期参数**。
查询上下文(Context)的主要作用是获取附加到Schema的全局数据**需要注意的是如果你的Resolver函数返回的数据借用了Context内保存的数据需要明确指定生命周期参数**。
下面是一个返回值借用Context内数据的例子

View File

@ -5,7 +5,7 @@ Relay定义了一套游标连接规范以提供一致性的分页查询方式
在`Async-graphql`中定义一个连接非常简单,只需要两步:
1. 实现`async_graphql::DataSource`,重写`query_operation`函数。
2. 在字段的Resolve函数中调用`DataSource::query`函数并传递响应参数。
2. 在字段的Resolver函数中调用`DataSource::query`函数并传递响应参数。
下面是一个简单的获取连续整数的数据源:

View File

@ -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`中获取一个数据库连接。

View File

@ -1,6 +1,6 @@
# 接口(Interface)
接口用于抽象具有特定字段集合的对象,`Async-graphql`内部实现实际上是一个包装器包装器转发接口上定义的Resolve函数到实现该接口的对象所以接口类型所包含的字段类型参数都必须和实现该接口的对象完全匹配。
接口用于抽象具有特定字段集合的对象,`Async-graphql`内部实现实际上是一个包装器包装器转发接口上定义的Resolver函数到实现该接口的对象,所以接口类型所包含的字段类型,参数都必须和实现该接口的对象完全匹配。
`Async-graphql`自动实现了对象到接口的转换,把一个对象类型转换为接口类型只需要调用`Into::into`。

View File

@ -1,6 +1,6 @@
# 简单对象(SimpleObject)
简单对象是把Rust结构的所有字段都直接映射到GraphQL对象不支持定义单独的Resolve函数。
简单对象是把Rust结构的所有字段都直接映射到GraphQL对象不支持定义单独的Resolver函数。
下面的例子定义了一个名称为MyObject的对象包含字段`a`和`b``c`由于标记为`#[field(skip)]`所以不会映射到GraphQL。

View File

@ -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::Value>`,如果它是一个`serde_json::Map`,则可以扩展错误的信息。
Resolver函数类似这样
```rust
async fn parse_with_extensions(&self) -> Result<i32, FieldError> {
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<i32> {
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<i32> {
// 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<i32> {
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<i32> {
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`实现,代替`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) -> FieldResult<i32> {
// 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<i32> {
// does work because ErrorExtensions is implemented for &ParseIntError
"234a"
.parse()
.map_err(|ref e: ParseIntError| e.extend_with(|_| json!({"code": 404})))
}
```

View File

@ -1,6 +1,6 @@
# 错误处理
Resolve函数可以返回一个FieldResult类型以下是FieldResult的定义
Resolver函数可以返回一个FieldResult类型以下是FieldResult的定义
```rust
type FieldResult<T> = std::result::Result<T, FieldError>;

View File

@ -2,7 +2,7 @@
## 查询根对象
查询根对象是一个GraphQL对象定义类似其它对象。查询对象的所有字段Resolve函数是并发执行的。
查询根对象是一个GraphQL对象定义类似其它对象。查询对象的所有字段Resolver函数是并发执行的。
```rust
use async_graphql::*;

View File

@ -1,6 +1,6 @@
# 订阅
订阅根对象和其它根对象定义稍有不同它的Resolve函数总是返回一个`Stream`,而字段参数通常作为数据的筛选条件。
订阅根对象和其它根对象定义稍有不同它的Resolver函数总是返回一个`Stream`,而字段参数通常作为数据的筛选条件。
下面的例子订阅一个整数流,它每秒产生一个整数,参数`step`指定了整数的步长默认为1。