mvc6中app、服务和中间件的区别

Difference between app, services and middleware in mvc6

我想了解 MVC6 中中间件的概念。这对我来说仍然有点模糊。我真的看不出你在 Startup class.

中获得的几个 "standard" 变量之间的区别

据我所知,有 3 种不同的方法可以告诉应用程序它应该使用特定的中间件?

您可以通过服务调用中间件使用。但这似乎只适用于 "adding" 中间件?

services.AddMvc();

// Add other services
services.AddScoped<IMyCountriesRepository, MyCountriesRepository>();
services.AddScoped<IEmailer, Emailer>();

那么你有IApplicationBuilder app。这是要实际使用服务中加载的中间件?所以你可以这样称呼它:

app.UseMvc();
app.UseErrorPage(...);
app.UseIdentity(); // cookie authentication 

然后有一种方法可以像这样加载和使用中间件:

app.UseMiddleware<MyCustomMiddleware>();

注册/使用三种中间件有什么好处?它们之间的确切区别是什么?

构建管道分为两个阶段:

  • 正在为 DI 注册服务
  • 将中间件添加到管道

AddMvc 注册 MVC 需要的服务(例如,视图引擎、JSON 格式化程序等)但不向管道添加任何内容。

UseMiddleware<T> 是将中间件添加到管道的通用方法。此方法将使用 DI 系统通过中间件的构造函数注入依赖项 class。

UseMvc等都是扩展方法,可以更方便的传入配置选项。如果您编写自定义中间件,您可以只调用 UseMiddleware<T> 或提供扩展方法,具体取决于您需要如何设置中间件。

您可以在此处找到更多信息:https://docs.asp.net/en/latest/fundamentals/middleware.html

我会区分添加服务和添加中间件。

添加服务

这基本上是将您的功能所需的 类 注册到 ASP .Net 5 中内置的依赖注入容器中。(IServiceCollection 接口)

您可以做的最简单的事情就是手动将它们一一添加,如下所示:

services.AddScoped<IMyCountriesRepository, MyCountriesRepository>();
services.AddScoped<IEmailer, Emailer>();

如果您正在构建一个更复杂的应用程序,或者一个自包含的框架,您可能想要创建一个函数来注册所有需要的服务。一个好的方法是创建一个扩展方法:

public static void AddMyServices(this IServiceCollection services)
{
    services.AddScoped<IMyCountriesRepository, MyCountriesRepository>();
    services.AddScoped<IEmailer, Emailer>();
    ...
}

//register all your services just by running the ext method:
services.AddMyServices();

这正是 services.AddMvc(); 正在做的事情。

In a more flexible way as it allows you to pass a lambda to further customize default services like the model binders (Like services.AddMvc(opts => opts.ModelBinders ...)) and is returning an IMvcBuilder you can use to further customize it things like the view engines (Like services.AddMvc().AddViewOptions(opts => opts.ViewEngines ...)).

添加中间件

ASP .Net 5 不是基于 HTTP 模块和处理程序,而是基于中间件的 OWIN 思想。 Andrei Dzimchuknice blog entry 描述了中间件,很好地总结了它:

Middleware – Pass through components that form a pipeline between a server and application to inspect, route, or modify request and response messages for a specific purpose.

And this definition applies to ASP.NET 5 as well. Middleware can be thought of as both HTTP modules and handlers that we've had in classic ASP.NET. Some middleware would implement various intermediate tasks when processing requests such as authentication, session state retrieval and persistence, logging and so on. Some of them would be the ultimate request handlers that would produce responses.

所以现在您想将自己的行为添加到 ASP 管道中。

最简单的就是定义一个内联中间件:

app.Use(async (context, next) =>
{
    //do something before passing the request to the next middleware
    await next.Invoke();
});

您也可以create your own middleware class注册:

app.UseMiddleware<MyMiddleware>();

最后,您可以再次定义扩展方法来封装复杂的设置逻辑。

This is what app.UseMvc() does. It lets you define your routes and it is then adding the routing middleware by calling app.UseRouter(). As you can see, the implementation of app.UseRouter adds the RouterMiddleware into the pipeline with the call to builder.UseMiddleware<RouterMiddleware>(router);

您的中间件所需的任何服务之前都已注册。这意味着它们将通过内置的 DI 容器供您的中间件使用。


最终结果是该框架使您可以更轻松地混合和匹配应用程序所需的组件(服务)和行为(中间件),仅包括您需要的部分。

我想在大牛的回答中添加一个实际的例子。 (他的回答很详细很正确,先看看这个)

TL;DR:

services.Add与中间件没有直接关系。这是关于在依赖注入容器中注册依赖项。

app.Use 是关于挑选哪些代码将 运行 在管道中(执行逻辑),以何种顺序,以及是否允许管道继续处理。想象力是极限,一个例子是编写一个中间件,它根据 IP 地址显示一个页面:'sorry service is not available in your country')

app.UseMiddleware 它与 app.Use 相同,但不是声明内联代码,而是指定一个 class 将具有将为您调用的 Invoke 方法。

现在,让我们来看一些示例代码:

假设您希望应用程序处理您的输出或部分输出,例如缩小 HTML.

您可以添加一个中间件,在将响应写入输出之前拦截响应并将其缩小。

所以你可以使用:

app.Use(async (context, next) =>
{
    await next(context);
    context.Response // will have the response as processed by all the previous middleswares like mvc.
    if IsMinifiable(context.Response)
    MinifyResponse(context.Response);

});

如果您想在各种应用程序中或由其他人共享您的中间件,您可能想要创建一个中间件并更像这样使用它:

app.UseMiddleware<HtmlMinifierMiddleware>(); 

这将在配置方法中使用一行代码为您完成所有工作。通常的做法是运送扩展方法,例如 app.UseHtmlMinifier() 和 return 可以链接以进行配置或支持配置参数的特定对象。使用扩展提供了很大的灵活性、可读性和 api 可发现性 :D

现在假设您的中间件是这样声明的:

public class HtmlMinifierMiddleware {
    public HtmlMinifier(IHtmlMinifier minifier) {
        // ...
    }
    public string Minify(string content) {
        return minifier.Minify(content);
    }
    // ...
}

如你所见,你需要传递一个IHtmlMinifer,所以你需要为DI注册它。

这是在 ConfigureService 上完成的,例如:

services.AddScoped<IHtmlMinifier, MyCoolHtmlMinifier>();

现在假设您需要的不是 1 个,而是许多依赖项,这将取决于中间件的 developer/consumer 来了解需要注册的每个依赖项。

中间件的作者通常会提供一个扩展来简化开发人员的使用,例如:services.AddHtmlMinifier() 正是这样,一种将服务注册到 DI 容器中的扩展方法。

即使您不使用中间件,您也可以使用相同的模式利用您自己的应用程序的依赖性。

例如,如果您的应用是电子商务,您可以创建扩展方法来注册您的依赖项:services.AddProductManagement()services.AddPriceCalculator()services.AddSearching() 等,或者只是 services.AddMyCoolApplication() 提供一种干净的方式来添加(注册)您的服务(依赖项),DI 容器会为您的应用程序找到这些服务。