查询上下文 (Context)

Context的主要目标是获取附加到Schema的全局数据或者与正在处理的实际查询相关的数据。

存储数据

Context中你可以存放全局数据,例如环境变量、数据库连接池,以及你在每个查询中可能需要的任何内容。

数据必须实现SendSync

你可以通过调用ctx.data::<TypeOfYourData>()来获取查询中的数据。

主意:如果 Resolver 函数的返回值是从Context中借用的,则需要明确说明参数的生命周期。

下面的例子展示了如何从Context中借用数据。

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;

struct Query;

#[Object]
impl Query {
    async fn borrow_from_context_data<'ctx>(
        &self,
        ctx: &Context<'ctx>
    ) -> Result<&'ctx String> {
        ctx.data::<String>()
    }
}
}

Schema 数据

你可以在创建Schema时将数据放入上下文中,这对于不会更改的数据非常有用,例如连接池。

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;
#[derive(Default,SimpleObject)]
struct Query { version: i32}
struct EnvStruct;
let env_struct = EnvStruct;
struct S3Object;
let s3_storage = S3Object;
struct DBConnection;
let db_core = DBConnection;
let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription)
    .data(env_struct)
    .data(s3_storage)
    .data(db_core)
    .finish();
}

请求数据

你可以在执行请求时将数据放入上下文中,它对于身份验证数据很有用。

一个使用warp的小例子:

#![allow(unused)]
fn main() {
extern crate async_graphql;
extern crate async_graphql_warp;
extern crate warp;
use async_graphql::*;
use warp::{Filter, Reply};
use std::convert::Infallible;
#[derive(Default, SimpleObject)]
struct Query { name: String }
struct AuthInfo { pub token: Option<String> }
let schema = Schema::build(Query::default(), EmptyMutation, EmptySubscription).finish();
let schema_filter = async_graphql_warp::graphql(schema);
let graphql_post = warp::post()
  .and(warp::path("graphql"))
  .and(warp::header::optional("Authorization"))
  .and(schema_filter)
  .and_then( |auth: Option<String>, (schema, mut request): (Schema<Query, EmptyMutation, EmptySubscription>, async_graphql::Request)| async move {
    // Do something to get auth data from the header
    let your_auth_data = AuthInfo { token: auth };
    let response = schema
      .execute(
        request
         .data(your_auth_data)
      ).await;
      
    Ok::<_, Infallible>(async_graphql_warp::GraphQLResponse::from(response))
  });
}

HTTP 头

使用Context你还可以插入或添加 HTTP 头。

#![allow(unused)]
fn main() {
extern crate async_graphql;
extern crate http;
use ::http::header::ACCESS_CONTROL_ALLOW_ORIGIN;
use async_graphql::*;
struct Query;
#[Object]
impl Query {
    async fn greet(&self, ctx: &Context<'_>) -> String {
        // Headers can be inserted using the `http` constants
        let was_in_headers = ctx.insert_http_header(ACCESS_CONTROL_ALLOW_ORIGIN, "*");

        // They can also be inserted using &str
        let was_in_headers = ctx.insert_http_header("Custom-Header", "1234");

        // If multiple headers with the same key are `inserted` then the most recent
        // one overwrites the previous. If you want multiple headers for the same key, use
        // `append_http_header` for subsequent headers
        let was_in_headers = ctx.append_http_header("Custom-Header", "Hello World");

        String::from("Hello world")
    }
}
}

Selection / LookAhead

有时你想知道子查询中请求了哪些字段用于优化数据处理,则可以使用ctx.field()读取查询中的字段,它将提供一个SelectionField,允许你在当前字段和子字段之间导航。

如果要跨查询或子查询执行搜索,则不必使用 SelectionField 手动执行此操作,可以使用 ctx.look_ahead() 来执行选择。

#![allow(unused)]
fn main() {
extern crate async_graphql;
use async_graphql::*;

#[derive(SimpleObject)]
struct Detail {
    c: i32,
    d: i32,
}

#[derive(SimpleObject)]
struct MyObj {
    a: i32,
    b: i32,
    detail: Detail,
}

struct Query;

#[Object]
impl Query {
    async fn obj(&self, ctx: &Context<'_>) -> MyObj {
        if ctx.look_ahead().field("a").exists() {
            // This is a query like `obj { a }`
        } else if ctx.look_ahead().field("detail").field("c").exists() {
            // This is a query like `obj { detail { c } }`
        } else {
            // This query doesn't have `a`
        }
        unimplemented!()
    }
}
}