在 SOLID 设计原则中,D 表示依赖反转原则
- 高层组件不应该依赖于底层组件,双方应该基于抽象
- 抽象不应该依赖于实现,实现应该依赖于抽象
使用 new 操作符来创建对象实例会导致不同组件之间的紧耦合。为了保持它们之间的松耦合,我们应该遵循依赖反转原则。这样模块将不依赖与具体的实现,而是依赖于抽象,例如接口。
抽象可以有用多种不同的实现,所以,当遇到抽象的时候,应该有某种方式提供某个特定的实现。我们称负责管理这类问题的类为依赖注入容器,它应该可以通过某种方式进行配置。
ASP.NET Core 提供了内置的依赖注入容器。它简单但是对我们的需求已经足够好用。它不仅可以配置抽象对应的实现,还可以控制其所创建对象的生命周期。
当前,对于我们简单的 Hello, World 应用来说,我们还不关心生命周期,所以,我们将对所有的实例仅仅使用 Singleton 单例模式。
我们不再实例化 DocumentWriter 和 DocumentExecutor,我们可以使用它们的抽象接口 IDocumentWriter 和 IDocumentExecutor。因此,我们可以使用内置的依赖注入容器如下配置:
public void ConfigureServices(IServiceCollection services) { services.AddSingleton<IDocumentWriter, DocumentWriter>(); services.AddSingleton<IDocumentExecuter, DocumentExecuter>(); }
对于 HelloWordQuery,我们没有定义接口,我们也可以直接使用这个实现。
services.AddSingleton<HelloWordQuery>();
Schema 包含 Query,以后我们还会增加 Mutation 和其它字段。所以,我们最好创建一个独立的类来管理它。该类将扩展 Schema 类型,我们通过构造函数进行依赖注入。
public class HelloWorldSchema : Schema { public HelloWorldSchema(HelloWorldQuery query) { Query = query; } }
最后,我们在 ConfigureServices 方法中配置 HelloWorldSchema 。
services.AddSingleton<ISchema, HelloWorldSchema>();
注:ISchema 来自 GraphQL 库。
现在,我们可以将中间件的代码分离到它自己的类中。如下代码我们命名为 GraphQLMiddleware。
public class GraphQLMiddleware { private readonly RequestDelegate _next; private readonly IDocumentWriter _writer; private readonly IDocumentExecuter _executor; private readonly ISchema _schema; public GraphQLMiddleware(RequestDelegate next, IDocumentWriter writer, IDocumentExecuter executor, ISchema schema) { _next = next; _writer = writer; _executor = executor; _schema = schema; } public async Task InvokeAsync(HttpContext httpContext) { if (httpContext.Request.Path.StartsWithSegments("/api/graphql") && string.Equals(httpContext.Request.Method, "POST", StringComparison.OrdinalIgnoreCase)) { string body; using (var streamReader = new StreamReader(httpContext.Request.Body)) { body = await streamReader.ReadToEndAsync(); var request = JsonConvert.DeserializeObject<GraphQLRequest>(body); var result = await _executor.ExecuteAsync(doc => { doc.Schema = _schema; doc.Query = request.Query; }).ConfigureAwait(false); var json = _writer.Write(result); await httpContext.Response.WriteAsync(json); } } else { await _next(httpContext); } } }
注意,我们将所有实例类型替换为抽象类型,使我们代码松耦合。每个可注入的服务通过构造函数进行注入。
最后但不是最后一步,我们必须将中间件连接到应用处理管线中。IApplicationBuilder 拥有名为 UseMiddleware 的扩展方法,以用于连接中间件,所以,最后的 Configure 方法如下所示:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app.UseMiddleware<GraphQLMiddleware>(); }