GraphQL Part II: 中间件
如果您熟悉 ASP.NET Core 的中间件,您可能注意到在我们上一篇博客中我们已经拥有了一个中间件。在初始的空白应用中,中间件的职责是返回 hello World 响应。后来, 我们用我们的自定义代码替换了它, 以便它可以响应一些静态 GraphQL 查询的结果。
中间件是一种可以装配到应用处理管线中的软件,以便处理请求和响应。每个组件包括:
- 选择是否将请求传递给管线中的下一个组件
- 可以在管线调用下一个组件之前或者之后执行任务
事实上,中间件是一个代理,或者更精确地说,他是一个请求代理。如名字所提示,它处理传入的请求并决定是否传递到管线中的下一个中间件。在我们的案例中,我们使用 IApplicationBuilder 的 run() 扩展方法来配置请求代理。在 3 个扩展方法之间 ( use, run, map ),Run() 方法在请求管线中终止了进一步的请求处理。
我们中间件中的代码非常简单,仅仅返回硬编码的静态查询,但是,在真实场景下,query 应该是动态的,我们必须从传入的请求中获取它。
每个请求代理接受一个 HttpContet 对象。如果 query 是通过 HTTP 的 Post 方式提交,您可以使用如下代码读取请求体。
string body; using (var streamReader = new StreamReader(httpContext.Request.Body)) { body = await streamReader.ReadToEndAsync(); }
在读取其内容之前做校验工作,就不会带来危害。所以,让我们放置一个 if 来检查两件事:
- 它是 Post 请求吗?
- 它来自特定的 URL 地址吗?
我们的代码做如下修改:
if(context.Request.Path.StartsWithSegments("/api/graphql") &&
string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { string body; using (var streamReader = new StreamReader(context.Request.Body)) { body = await streamReader.ReadToEndAsync(); }
......
请求体重可以包含大量字段,但是,我们可以说传入的 GraphQL 查询在名为 query 的字段内。所以,我们可以解析 body 的内容到一个包含 Query 属性的复杂类型示例中。
复杂类型如下所示:
public class GraphQLRequest { public string Query { get; set; } }
下一步要做的就是反序列化 body 内容到 GraphQLRequest 对象示例中,使用 Json.Net 的 JsonConvert.DeserializeObject 并替换前面硬编码的内容。
var request = JsonConvert.DeserializeObject<GraphQLRequest>(body); var result = await new DocumentExecuter().ExecuteAsync(doc => { doc.Schema = schema; doc.Query = request.Query; }).ConfigureAwait(false);
在所有的修改之后,在 Startup.cs 中的 Run 方法如下所示:
app.Run(async (context) => { if (context.Request.Path.StartsWithSegments("/api/graphql") && string.Equals(context.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { string body; using (var streamReader = new StreamReader(context.Request.Body)) { body = await streamReader.ReadToEndAsync(); var request = JsonConvert.DeserializeObject<GraphQLRequest>(body); var schema = new Schema { Query = new HelloWorldQuery() }; var result = await new DocumentExecuter().ExecuteAsync(doc => { doc.Schema = schema; doc.Query = request.Query; }).ConfigureAwait(false); var json = new DocumentWriter(indent: true).Write(result); await context.Response.WriteAsync(json); } } });
现在,可以使用任何客户端 ( Postman 或者 Insomnia ) 发送一个包含 query 字段的 POST 请求.
我们几乎完成了工作,但是您可以看到大量的 new 代码创建对象,例如:new DocumentExecuter(),new Schema(), new DocumentWriter() 等等,下一节,我们将使用 ASP.NET Core 内置的依赖注入系统注入它们。
上一篇:GraphQL Part I: hello, world.