根据 单一数据源架构模式,graphql 只是接口层,用户授权相关是在业务逻辑层,通常使用 Http 框架的中间件实现。 如果 graphql 需要消费 web 框架生成的数据,这时就需要手动数据上下文转换。 网站入口 Poem 应用初始化 // graphql schema let schema = build_schema().await; // 请求上下文全局状态数据 let state = State { schema, // 初始的空数据也可以使用 Option 类型 req_ctx: init_default_req_ctx(), }; // poem应用初始化 let app = Route::new() // 绑定graphql 路由 .at(path, get(graphiql).post(graphql)) // 权限检测中间件 .with(AuthMiddleware) // 当前用户信息和请求上下文数据整理 .with(CtxMiddleware) .data(state); Poem 中间件数据处理 自定义 Context 中间件,主要对当前用户信息和请求体参数格式化 /// req上下文注入中间件 同时进行jwt授权验证 pub struct Context; impl<E: Endpoint> Middleware<E> for Context { type Output = ContextEndpoint<E>; fn transform(&self, ep: E) -> Self::Output { ContextEndpoint { inner: ep } } } /// Endpoint for `Tracing` middleware. pub struct ContextEndpoint<E> { inner: E, } #[poem::async_trait] impl<E: Endpoint> Endpoint for ContextEndpoint<E> { type Output = E::Output; // type Output = Response; async fn call(&self, req: Request) -> Result<Self::Output> { // Http全局状态数据获取 let mut state: Option<State> = None; { if let Some(state_data) = req.extensions().get::<State>() { state = Some(state_data.clone()); } } // 请求信息ctx注入 let auth_token = get_request_auth_token(&req); // 解析JWT TOKEN let login_info= decode_jwt( auth_token.as_str(), !gql_is_public_query ).unwrap(); // 生成新的请求上下文数据 let req_ctx = ReqCtx { ori_uri: if path_params.is_empty() { ori_uri_path } else { ori_uri_path + "?" + &path_params }, path, path_params, method: method.clone(), login_info, data: body_data.clone(), gql_operation_name, gql_variables, gql_trimmed_query, gql_is_public_query, }; // 生成新的 Request对象 let mut req = Request::from_parts(req_parts, Body::from(bytes)); if let Some(state) = state { // 移除旧的全局State数据 req.extensions_mut().remove::<State>(); // 生成新的 State let new_state = State { schema: state.schema.clone(), req_ctx, }; req.extensions_mut().insert(new_state); } // 开始请求数据 self.inner.call(req).await } } 上下文数据注入到 graphql #[handler] pub async fn graphql( data: Data<&State>, gql_req: GraphQLRequest, ) -> GraphQLResponse { let mut gql_req = gql_req.0; let schema = data.0.schema.clone(); // 将 poem 中生成请求上下文转入 graphql gql_req = gql_req.data(data.0.req_ctx.clone()); schema.execute(gql_req).await.into() } Graphql 中消费数据 通过上面的数据注入,在 Graphql 上文中获取到对应的数据类型 async fn logout(&self, ctx: &Context<'_>) -> Result<bool> { // 从graphql 上下文获取对应的数据类型 let req_ctx = ctx.data_unchecked::<ReqCtx>(); println!("req-ctx-----{:?}", req_ctx); Ok(true) }