Update documents

This commit is contained in:
Sunli 2021-11-16 14:51:20 +08:00
parent 5549c85f55
commit 3d8a8ef6af
20 changed files with 277 additions and 368 deletions

View File

@ -54,11 +54,11 @@ pub fn generate_guards(
let expr: Expr =
syn::parse_str(code).map_err(|err| Error::new(code.span(), err.to_string()))?;
let code = quote! {{
use #crate_name::guard::GuardExt;
use #crate_name::GuardExt;
#expr
}};
Ok(quote! {
#crate_name::guard::Guard::check(&#code, &ctx).await #map_err ?;
#crate_name::Guard::check(&#code, &ctx).await #map_err ?;
})
}

View File

@ -31,7 +31,7 @@
- [How extensions are working](extensions_inner_working.md)
- [Available extensions](extensions_available.md)
- [Integrations](integrations.md)
- [Tide](integrations_to_tide.md)
- [Poem](integrations_to_poem.md)
- [Warp](integrations_to_warp.md)
- [Actix-web](integrations_to_actix_web.md)
- [Advanced topics](advanced_topics.md)

View File

@ -91,3 +91,14 @@ pub struct YetAnotherObject {
```
You can pass multiple generic types to `params()`, separated by a comma.
## Used for both input and output
```rust
#[derive(SimpleObject, InputObject)]
#[graphql(input_name = "MyObjInput")] // Note: You must use the input_name attribute to define a new name for the input type, otherwise a runtime error will occur.
struct MyObj {
a: i32,
b: i32,
}
```

View File

@ -1,7 +1,6 @@
# Field Guard
You can define a `guard` to a field of an `Object`. This permits you to add checks before runing the code logic of the field.
`Guard` are made of rules that you need to define before. A rule is a structure which implements the trait `Guard`.
You can define a `guard` for the fields of `Object`, `SimpleObject`, `ComplexObject` and `Subscription`, it will be executed before calling the resolver function, and an error will be returned if it fails.
```rust
#[derive(Eq, PartialEq, Copy, Clone)]
@ -14,6 +13,12 @@ struct RoleGuard {
role: Role,
}
impl RoleGuard {
fn new(role: Role) -> Self {
Self { role }
}
}
#[async_trait::async_trait]
impl Guard for RoleGuard {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
@ -26,43 +31,54 @@ impl Guard for RoleGuard {
}
```
Once you have defined your rule you can use it in the `guard` field attribute.
This attribute supports 4 operators to create complex rules :
- `and` : perform an `and` operation between two rules. (If one rule returns an error the `and` operator will return the error. If both rules return an error it's the first one that will be returned).
- `or` : performs an `or` operation between two rules. (If both rules return an error the error returned is the first one)
- `chain` : takes a set of rules and runs them until one returns an error or it returns `Ok` if all rules pass.
- `race` : takes a set of rules and runs them until one returns `Ok` if they all fail, it returns the last error.
Use it with the `guard` attribute:
```rust
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
#[graphql(guard(and(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#)
)))]
/// Only allow Admin
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
value1: i32,
/// Allow Admin or Guest
#[graphql(guard = "RoleGuard::new(Role::Admin).or(RoleGuard::new(Role::Guest))")]
value2: i32,
#[graphql(guard(or(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#)
)))]
value3: i32,
#[graphql(guard(chain(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value4: i32,
#[graphql(guard(race(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value5: i32,
}
```
## Use parameter value
Sometimes guards need to use field parameters, you need to pass the parameter value when creating the guard like this:
```rust
struct EqGuard {
expect: i32,
actual: i32,
}
impl EqGuard {
fn new(expect: i32, actual: i32) -> Self {
Self { expect, actual }
}
}
#[async_trait::async_trait]
impl Guard for EqGuard {
async fn check(&self, _ctx: &Context<'_>) -> Result<()> {
if self.expect != self.actual {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct Query;
#[Object]
impl Query {
#[graphql(guard = "EqGuard::new(100, value)")]
async fn get(&self, value: i32) -> i32 {
value
}
}
```

View File

@ -1,117 +1,78 @@
# Input value validators
Arguments to a query ([InputObject](define_input_object.md)) are called `Input Objects` in GraphQL. If the provided input type does not match for a query, the query will return a type mismatch error. But, sometimes we want to provide more restrictions on specific types of values. For example, we might want to require that an argument is a valid email address. `Async-graphql` provides an input validators to solve this problem.
`Async-graphql` has some common validators built-in, you can use them on the parameters of object fields or on the fields of `InputObject`.
An input validator can be combined via `and` and `or` operators.
The following is an input validator which checks that a `String` is a valid Email or MAC address:
- **maximum=N** the number cannot be greater than `N`.
- **minimum=N** the number cannot be less than `N`.
- **multiple_of=N** the number must be a multiple of `N`.
- **max_items=N** the length of the list cannot be greater than `N`.
- **min_items=N** the length of the list cannot be less than `N`.
- **max_length=N** the length of the string cannot be greater than `N`.
- **min_length=N** the length of the string cannot be less than `N`.
```rust
use async_graphql::*;
use async_graphql::validators::{Email, MAC};
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(or(Email, MAC(colon = "false"))))] a: String) {
/// The length of the name must be greater than or equal to 5 and less than or equal to 10.
async fn input(#[graphql(validator(min_length = 5, max_length = 10))] name: String) {
}
}
```
The following example verifies that the `i32` parameter `a` is greater than 10 and less than 100, or else equal to 0:
## Check every member of the list
You can enable the `list` attribute, and the validator will check all members in list:
```rust
use async_graphql::*;
use async_graphql::validators::{IntGreaterThan, IntLessThan, IntEqual};
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(
or(
and(IntGreaterThan(value = "10"), IntLessThan(value = "100")),
IntEqual(value = "0")
)))] a: String) {
}
}
```
## Validate the elements of the list.
You can use the `list` operator to indicate that the internal validator is used for all elements in a list:
```rust
use async_graphql::*;
use async_graphql::validators::Email;
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(list(Email)))] emails: Vec<String>) {
async fn input(#[graphql(validator(list, max_length = 10))] names: Vec<String>) {
}
}
```
## Custom validator
Here is an example of a custom validator:
```rust
struct MustBeZero {}
struct MyValidator {
expect: i32,
}
impl InputValueValidator for MustBeZero {
fn is_valid(&self, value: &Value) -> Result<(), String> {
if let Value::Int(n) = value {
if n.as_i64().unwrap() != 0 {
// Validation failed
Err(format!(
"the value is {}, but must be zero",
n.as_i64().unwrap(),
))
} else {
// Validation succeeded
Ok(())
}
} else {
// If the type does not match we can return None and built-in validations
// will pick up on the error
impl MyValidator {
pub fn new(n: i32) -> Self {
MyValidator { expect: n }
}
}
#[async_trait::async_trait]
impl CustomValidator<i32> for MyValidator {
async fn check(&self, _ctx: &Context<'_>, value: &i32) -> Result<(), String> {
if *value == self.expect {
Ok(())
} else {
Err(format!("expect 100, actual {}", value))
}
}
}
```
Here is an example of a custom validator with extensions (return `async_graphql::Error`):
struct Query;
```rust
pub struct Email;
impl InputValueValidator for Email {
fn is_valid_with_extensions(&self, value: &Value) -> Result<(), Error> {
if let Value::String(s) = value {
if &s.to_lowercase() != s {
return Err(Error::new("Validation Error").extend_with(|_, e| {
e.set("key", "email_must_lowercase")
}));
}
if !validate_non_control_character(s) {
return Err(Error::new("Validation Error").extend_with(|_, e| {
e.set("key", "email_must_no_non_control_character")
}));
}
if !validate_email(s) {
return Err(Error::new("Validation Error").extend_with(|_, e| {
e.set("key", "invalid_email_format")
}));
}
}
Ok(())
#[Object]
impl Query {
/// n must be equal to 100
async fn value(
&self,
#[graphql(validator(custom = "MyValidator::new(100)"))] n: i32,
) -> i32 {
n
}
}
```

View File

@ -5,7 +5,7 @@
- Poem [async-graphql-poem](https://crates.io/crates/async-graphql-poem)
- Actix-web [async-graphql-actix-web](https://crates.io/crates/async-graphql-actix-web)
- Warp [async-graphql-warp](https://crates.io/crates/async-graphql-warp)
- Tide [async-graphql-tide](https://crates.io/crates/async-graphql-tide)
- Rocket [async-graphql-rocket](https://github.com/async-graphql/async-graphql/tree/master/integrations/rocket)
- Axum [async-graphql-axum](https://crates.io/crates/async-graphql-axum)
- Rocket [async-graphql-rocket](https://crates.io/crates/async-graphql-rocket)
**Even if the server you are currently using is not in the above list, it is quite simple to implement similar functionality yourself.**

View File

@ -1,10 +1,4 @@
# Actix-web
`Async-graphql-actix-web` provides an implementation of `actix_web::FromRequest` for `Request`.
This is actually an abstraction around `async_graphql::Request` and you can call `Request::into_inner` to
convert it into a `async_graphql::Request`.
`WSSubscription` is an Actor that supports WebSocket subscriptions.
# Poem
## Request example
@ -14,11 +8,10 @@ When you define your `actix_web::App` you need to pass in the Schema as data.
async fn index(
// Schema now accessible here
schema: web::Data<Schema>,
request: async_graphql_actix_web::Request,
) -> web::Json<Response> {
web::Json(Response(schema.execute(request.into_inner()).await)
request: GraphQLRequest,
) -> web::Json<GraphQLResponse> {
web::Json(schema.execute(request.into_inner()).await.into())
}
```
## Subscription example
@ -29,7 +22,7 @@ async fn index_ws(
req: HttpRequest,
payload: web::Payload,
) -> Result<HttpResponse> {
WSSubscription::start(Schema::clone(&*schema), &req, payload)
GraphQLSubscription::new(Schema::clone(&*schema)).start(&req, payload)
}
```

View File

@ -0,0 +1,25 @@
# Poem
## Request example
```rust
use poem::Route;
use async_graphql_poem::GraphQL;
let app = Route::new()
.at("/ws", GraphQL::new(schema));
```
## Subscription example
```rust
use poem::Route;
use async_graphql_poem::GraphQLSubscription;
let app = Route::new()
.at("/ws", get(GraphQLSubscription::new(schema)));
```
## More examples
[https://github.com/async-graphql/examples/tree/master/poem](https://github.com/async-graphql/examples/tree/master/poem)

View File

@ -1,73 +0,0 @@
# Tide
`async_graphql_tide` provides an implementation of [tide::Endpoint](https://docs.rs/tide/0.15.0/tide/trait.Endpoint.html) trait. It also provides `receive_request` and `respond` functions to convert a Tide request to a GraphQL request and back to Tide response, if you want to handle the request manually.
## Request example
When you create your `tide` server, you will need to pass the `async_graphql_tide::endpoint` with your schema as the POST request handler. Please note that you need to enable the `attributes` feature in `async-std` for this example to work.
```rust
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
Context, EmptyMutation, EmptySubscription, Object, Schema, SimpleObject,
};
use tide::{http::mime, Body, Response, StatusCode};
#[derive(SimpleObject)]
pub struct Demo {
pub id: usize,
}
pub struct QueryRoot;
#[Object]
impl QueryRoot {
async fn demo(&self, _ctx: &Context<'_>) -> Demo {
Demo { id: 42 }
}
}
#[async_std::main]
async fn main() -> tide::Result<()> {
let mut app = tide::new();
// create schema
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish();
// add tide endpoint
app.at("/graphql")
.post(async_graphql_tide::endpoint(schema));
// enable graphql playground
app.at("/").get(|_| async move {
Ok(Response::builder(StatusCode::Ok)
.body(Body::from_string(playground_source(
// note that the playground needs to know
// the path to the graphql endpoint
GraphQLPlaygroundConfig::new("/graphql"),
)))
.content_type(mime::HTML)
.build())
});
Ok(app.listen("127.0.0.1:8080").await?)
}
```
## Manually handle the request
If you want to manually handle the request, for example to read a header, you can skip `async_graphql_tide::endpoint` and use `receive_request` and `respond` functions instead.
```rust
app.at("/graphql").post(move |req: tide::Request<()>| {
let schema = schema.clone();
async move {
let req = async_graphql_tide::receive_request(req).await?;
async_graphql_tide::respond(schema.execute(req).await)
}
});
```
## More examples
[https://github.com/async-graphql/examples/tree/master/tide](https://github.com/async-graphql/examples/tree/master/tide)

View File

@ -2,7 +2,7 @@
For `Async-graphql-warp`, two `Filter` integrations are provided: `graphql` and `graphql_subscription`.
The `graphql` filter is used for execution `Query` and `Mutation` requests. It always asks for the POST method and outputs a `async_graphql::Schema` and `async_graphql::Request`.
The `graphql` filter is used for execution `Query` and `Mutation` requests. It extracts GraphQL request and outputs `async_graphql::Schema` and `async_graphql::Request`.
You can combine other filters later, or directly call `Schema::execute` to execute the query.
`graphql_subscription` is used to implement WebSocket subscriptions. It outputs `warp::Reply`.

View File

@ -30,9 +30,9 @@
- [扩展如何工作](extensions_inner_working.md)
- [可用的扩展列表](extensions_available.md)
- [集成到WebServer](integrations.md)
- [Poem](integrations_to_poem.md)
- [Warp](integrations_to_warp.md)
- [Actix-web](integrations_to_actix_web.md)
- [Tide](integrations_to_tide.md)
- [高级主题](advanced_topics.md)
- [自定义标量](custom_scalars.md)
- [优化查询解决N+1问题](dataloader.md)

View File

@ -85,4 +85,15 @@ impl MyObj {
self.a + self.b
}
}
```
```
## 同时用于输入和输出
```rust
#[derive(SimpleObject, InputObject)]
#[graphql(input_name = "MyObjInput")] // 注意: 你必须用input_name属性为输入类型定义一个新的名称否则将产生一个运行时错误。
struct MyObj {
a: i32,
b: i32,
}
```

View File

@ -1,7 +1,7 @@
# 字段守卫(Field Guard)
您可以在给`Object`的字段定义`守卫`。 这允许在运行字段的代码逻辑之前添加检查
义`守卫`由你预先定义的规则组成。 规则是一种实现`Guard`特质的结构。
你可以为`Object`, `SimpleObject`, `ComplexObject`和`Subscription`的字段定义`守卫`,它将在调用字段的 Resolver 函数前执行,如果失败则返回一个错误
```rust
#[derive(Eq, PartialEq, Copy, Clone)]
enum Role {
@ -13,6 +13,12 @@ struct RoleGuard {
role: Role,
}
impl RoleGuard {
fn new(role: Role) -> Self {
Self { role }
}
}
#[async_trait::async_trait]
impl Guard for RoleGuard {
async fn check(&self, ctx: &Context<'_>) -> Result<()> {
@ -25,44 +31,54 @@ impl Guard for RoleGuard {
}
```
一旦定义了规则,就可以在`guard`字段属性中使用它。
此属性支持4个运算符创建复杂的规则
-`and`:在两个规则之间执行`与`运算。 (如果一个规则返回错误,则`and`运算符将返回错误。如果两个规则均返回错误,则将是第一个返回的错误)。
-`or`:在两个规则之间执行`或`运算。 (如果两个规则都返回错误,则返回的错误是第一个)
-`chain`:采用一组规则并运行它们,直到返回错误或如果所有规则都通过则返回`Ok`。
-`race`:采用一组规则并运行它们,直到其中一个返回`Ok`。
用`guard`属性使用它:
```rust
#[derive(SimpleObject)]
struct Query {
#[graphql(guard(RoleGuard(role = "Role::Admin")))]
value: i32,
#[graphql(guard(and(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#)
)))]
/// 只允许Admin访问
#[graphql(guard = "RoleGuard::new(Role::Admin)")]
value1: i32,
/// 允许Admin或者Guest访问
#[graphql(guard = "RoleGuard::new(Role::Admin).or(RoleGuard::new(Role::Guest))")]
value2: i32,
#[graphql(guard(or(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#)
)))]
value3: i32,
#[graphql(guard(chain(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value4: i32,
#[graphql(guard(race(
RoleGuard(role = "Role::Admin"),
UserGuard(username = r#""test""#),
AgeGuard(age = r#"21"#)
)))]
value5: i32,
}
```
## 从参数中获取值
有时候守卫需要从字段参数中获取值,你需要像下面这样在创建守卫时传递该参数值:
```rust
struct EqGuard {
expect: i32,
actual: i32,
}
impl EqGuard {
fn new(expect: i32, actual: i32) -> Self {
Self { expect, actual }
}
}
#[async_trait::async_trait]
impl Guard for EqGuard {
async fn check(&self, _ctx: &Context<'_>) -> Result<()> {
if self.expect != self.actual {
Err("Forbidden".into())
} else {
Ok(())
}
}
}
struct Query;
#[Object]
impl Query {
#[graphql(guard = "EqGuard::new(100, value)")]
async fn get(&self, value: i32) -> i32 {
value
}
}
```

View File

@ -1,56 +1,40 @@
# 输入值校验器
字段的参数,[输入对象(InputObject)](define_input_object.md)的字段在GraphQL中称之为`Input Value`如果给出的参数值类型不匹配查询会报类型不匹配错误。但有时候我们希望给特定类型的值更多的限制例如我们希望一个参数总是一个格式合法的Email地址我们可以通过[自定义标量](custom_scalars.md)来解决,但这种类型如果很多,而且我们希望能够进行自由组合,比如一个`String`类型参数我们希望它既可以是Email地址也可以是一个手机号码自定义标量就特别麻烦。`Async-graphql`提供了输入值校验器来解决这个问题
`Async-graphql`内置了一些常用的校验器,你可以在对象字段的参数或者`InputObject`的字段上使用它们
输入值校验器可以通过`and`和`or`操作符自由组合。
下面时一个参数校验器,它校验`String`类型的参数`a`必须是一个合法Email或者MAC地址
- **maximum=N** 指定数字不能大于N
- **minimum=N** 指定数字不能小于N
- **multiple_of=N** 指定数字必须是N的倍数
- **max_items=N** 指定列表的长度不能大于N
- **min_items=N** 指定列表的长度不能小于N
- **max_length=N** 字符串的长度不能大于N
- **min_length=N** 字符串的长度不能小于N
```rust
use async_graphql::*;
use async_graphql::validators::{Email, MAC};
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(or(Email, MAC(colon = "false"))))] a: String) {
}
}
```
下面的例子校验`i32`类型的参数`a`必须大于10并且小于100或者等于0
```rust
use async_graphql::*;
use async_graphql::validators::{IntGreaterThan, IntLessThan, IntEqual};
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(
or(
and(IntGreaterThan(value = "10"), IntLessThan(value = "100")),
IntEqual(value = "0")
)))] a: String) {
/// name参数的长度必须大于等于5小于等于10
async fn input(#[graphql(validator(min_length = 5, max_length = 10))] name: String) {
}
}
```
## 校验列表成员
你可以用`list`操作符表示内部的校验器作用于一个列表内的所有成员:
你可以打开`list`属性表示校验器作用于一个列表内的所有成员:
```rust
use async_graphql::*;
use async_graphql::validators::{Email};
struct Query;
#[Object]
impl Query {
async fn input(#[graphql(validator(list(Email)))] emails: Vec<String>) {
async fn input(#[graphql(validator(list, max_length = 10))] names: Vec<String>) {
}
}
```
@ -58,25 +42,37 @@ impl Query {
## 自定义校验器
```rust
struct MustBeZero {}
struct MyValidator {
expect: i32,
}
impl InputValueValidator for MustBeZero {
fn is_valid(&self, value: &Value) -> Result<(), String> {
if let Value::Int(n) = value {
if n.as_i64().unwrap() != 0 {
// 校验失败
Err(format!(
"the value is {}, but must be zero",
n.as_i64().unwrap(),
))
} else {
// 校验通过
Ok(())
}
} else {
// 类型不匹配直接返回None内置校验器会发现这个错误
impl MyValidator {
pub fn new(n: i32) -> Self {
MyValidator { expect: n }
}
}
#[async_trait::async_trait]
impl CustomValidator<i32> for MyValidator {
async fn check(&self, _ctx: &Context<'_>, value: &i32) -> Result<(), String> {
if *value == self.expect {
Ok(())
} else {
Err(format!("expect 100, actual {}", value))
}
}
}
struct Query;
#[Object]
impl Query {
/// n的值必须等于100
async fn value(
&self,
#[graphql(validator(custom = "MyValidator::new(100)"))] n: i32,
) -> i32 {
n
}
}
```

View File

@ -5,6 +5,7 @@
- Poem [async-graphql-poem](https://crates.io/crates/async-graphql-poem)
- Actix-web [async-graphql-actix-web](https://crates.io/crates/async-graphql-actix-web)
- Warp [async-graphql-warp](https://crates.io/crates/async-graphql-warp)
- Tide [async-graphql-tide](https://crates.io/crates/async-graphql-tide)
- Axum [async-graphql-axum](https://crates.io/crates/async-graphql-axum)
- Rocket [async-graphql-rocket](https://crates.io/crates/async-graphql-rocket)
**即使你目前使用的Web Server不在上面的列表中自己实现类似的功能也相当的简单。**
**即使你目前使用的Web Server不在上面的列表中自己实现类似的功能也相当的简单。**

View File

@ -1,8 +1,8 @@
# Actix-web
`Async-graphql-actix-web`提供实现了`actix_web::FromRequest`的`Request`,它其实是`async_graphql::Request`的包装,你可以调用`Request::into_inner`把它转换成一个`async_graphql::Request`
`Async-graphql-actix-web`提供了`GraphQLRequest`提取器用于提取`GraphQL`请求,和`GraphQLResponse`用于输出`GraphQL`响应
`WSSubscription`是一个支持Web Socket订阅的Actor。
`GraphQLSubscription`用于创建一个支持Web Socket订阅的Actor。
## 请求例子
@ -11,11 +11,10 @@
```rust
async fn index(
schema: web::Data<Schema>,
request: async_graphql_actix_web::Request,
) -> web::Json<Response> {
web::Json(Response(schema.execute(request.into_inner()).await)
request: GraphQLRequest,
) -> web::Json<GraphQLResponse> {
web::Json(schema.execute(request.into_inner()).await.into())
}
```
## 订阅例子
@ -26,7 +25,7 @@ async fn index_ws(
req: HttpRequest,
payload: web::Payload,
) -> Result<HttpResponse> {
WSSubscription::start(Schema::clone(&*schema), &req, payload)
GraphQLSubscription::new(Schema::clone(&*schema)).start(&req, payload)
}
```

View File

@ -0,0 +1,25 @@
# Poem
## 请求例子
```rust
use poem::Route;
use async_graphql_poem::GraphQL;
let app = Route::new()
.at("/ws", GraphQL::new(schema));
```
## 订阅例子
```rust
use poem::Route;
use async_graphql_poem::GraphQLSubscription;
let app = Route::new()
.at("/ws", get(GraphQLSubscription::new(schema)));
```
## 更多例子
[https://github.com/async-graphql/examples/tree/master/poem](https://github.com/async-graphql/examples/tree/master/poem)

View File

@ -1,73 +0,0 @@
# Tide
`async_graphql_tide` 提供一个 trait [tide::Endpoint](https://docs.rs/tide/0.15.0/tide/trait.Endpoint.html) 的实现。如果您想手动处理请求的话,它还提供了 `receive_request``respond` 函数来将一个 Tide 请求转换为一个 GraphQL 请求,并返回 `tide::Response`
## Request example
当创建你的 `tide` 服务器时,你需要将 `Schema` 传递给 `async_graphql_tide::endpoint` 。请注意,你需要启用 `async-std` 中的 feature `attributes` 才能使这个例子正常工作。
```rust
use async_graphql::{
http::{playground_source, GraphQLPlaygroundConfig},
Context, EmptyMutation, EmptySubscription, Object, Schema, SimpleObject,
};
use tide::{http::mime, Body, Response, StatusCode};
#[derive(SimpleObject)]
pub struct Demo {
pub id: usize,
}
pub struct QueryRoot;
#[Object]
impl QueryRoot {
async fn demo(&self, _ctx: &Context<'_>) -> Demo {
Demo { id: 42 }
}
}
#[async_std::main]
async fn main() -> tide::Result<()> {
let mut app = tide::new();
// create schema
let schema = Schema::build(QueryRoot, EmptyMutation, EmptySubscription).finish();
// add tide endpoint
app.at("/graphql")
.post(async_graphql_tide::endpoint(schema));
// enable graphql playground
app.at("/").get(|_| async move {
Ok(Response::builder(StatusCode::Ok)
.body(Body::from_string(playground_source(
// note that the playground needs to know
// the path to the graphql endpoint
GraphQLPlaygroundConfig::new("/graphql"),
)))
.content_type(mime::HTML)
.build())
});
Ok(app.listen("127.0.0.1:8080").await?)
}
```
## Manually handle the request
如果你想手动处理请求,例如读取头,你可以跳过 `async_graphql_tide::endpoint` 而使用 `receive_request``respond` 函数。
```rust
app.at("/graphql").post(move |req: tide::Request<()>| {
let schema = schema.clone();
async move {
let req = async_graphql_tide::receive_request(req).await?;
async_graphql_tide::respond(schema.execute(req).await)
}
});
```
## More examples
[https://github.com/async-graphql/examples/tree/master/tide](https://github.com/async-graphql/examples/tree/master/tide)

View File

@ -2,7 +2,7 @@
`Async-graphql-warp`提供了两个`Filter``graphql`和`graphql_subscription`。
`graphql`用于执行`Query`和`Mutation`请求,他总是要求POST方法输出一个包含`async_graphql::Schema`和`async_graphql::Request的元组`你可以在之后组合其它Filter或者直接调用`Schema::execute`执行查询。
`graphql`用于执行`Query`和`Mutation`请求,它提取GraphQL请求然后输出一个包含`async_graphql::Schema`和`async_graphql::Request`元组你可以在之后组合其它Filter或者直接调用`Schema::execute`执行查询。
`graphql_subscription`用于实现基于Web Socket的订阅它输出`warp::Reply`。

View File

@ -162,6 +162,7 @@
mod base;
mod error;
mod guard;
mod look_ahead;
mod model;
mod request;
@ -175,7 +176,6 @@ pub mod context;
#[cfg_attr(docsrs, doc(cfg(feature = "dataloader")))]
pub mod dataloader;
pub mod extensions;
pub mod guard;
pub mod http;
pub mod resolver_utils;
pub mod types;
@ -213,6 +213,7 @@ pub use error::{
Error, ErrorExtensionValues, ErrorExtensions, InputValueError, InputValueResult,
ParseRequestError, PathSegment, Result, ResultExt, ServerError, ServerResult,
};
pub use guard::{Guard, GuardExt};
pub use look_ahead::Lookahead;
pub use registry::CacheControl;
pub use request::{BatchRequest, Request};