练习 - 创建自定义中间件

已完成

开发人员可以创建自定义中间件组件,以向 ASP.NET Core 应用中添加功能。 自定义中间件可以插入中间件管道中的任何位置,并可以与内置中间件组件一起使用,如本例中所示:

一个显示请求如何通过管道流动的图示。

你公司的网络运营团队正在排查生产环境中的性能问题。 你的团队负责人让你实现一些功能,以更好地支持应用的实时监视。 该应用应将请求详细信息记录到控制台。 对于每个请求,它应记录请求方法、路径和响应状态代码。

在本练习中,你将创建一个自定义中间件组件,用以将请求详细信息记录到控制台。

添加自定义中间件

让我们修改现有的 ASP.NET Core 应用,以包含用于将请求详细信息记录到控制台的自定义中间件。

  1. 打开 Program.cs 文件(如果尚未打开)。

  2. 紧接在 app.Run() 之前,插入以下代码:

    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    

    在上述代码中:

    • app.Use() 向管道添加一个自定义中间件组件。 该组件采用一个 HttpContext 对象和一个 RequestDelegate 对象作为参数。
    • delegate 将请求方法、路径和响应状态代码写入到控制台。
    • await next() 调用管道中的下一个中间件组件。

测试更改

  1. Ctrl+Shift+F5 重新生成并重启应用。

  2. 当浏览器窗口打开时,请注意根 URL 会显示“欢迎使用 Contoso!”

  3. /history 添加到 URL,然后按 Enter。 浏览器将重定向到 /about 页。

  4. 在 Visual Studio Code 中,按 Ctrl+Shift+P 打开命令面板。 搜索并选择“调试控制台:聚焦到“调试控制台”视图”,切换到底部面板中的“调试控制台”选项卡。 注意以下行:

    GET / 200
    GET /about 200
    

    对于每个请求,控制台输出会显示请求方法、路径和响应状态代码。 第一行显示了对根 URL 的请求,第二行显示了对 /about 页的请求。

    注意

    浏览器可能还会请求 /favicon.ico。 这是对网站图标的标准请求,可以忽略。

  5. 请让应用保持运行状态以完成下一个练习。

更改中间件的顺序

应用似乎正常工作,但存在问题。 你请求了 /history 页,但控制台输出没有显示它。 之所以发生此行为,是因为用于记录请求详细信息的自定义中间件组件是在 URL 重写器中间件之后添加的。 URL 重写器中间件将请求从 /history 重定向到 /about 并发送响应,自定义中间件组件看不到请求。 让我们来解决此问题。

  1. 将你添加的 app.Use() 行移动到 app.UseRewriter() 行的正前方。

    完整的 Program.cs 文件应如下所示:

    using Microsoft.AspNetCore.Rewrite;
    
    var builder = WebApplication.CreateBuilder(args);
    var app = builder.Build();
    
    app.Use(async (context, next) =>
    {
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
        await next(); 
    });
    
    app.UseRewriter(new RewriteOptions().AddRedirect("history", "about"));
    
    app.MapGet("/", () => "Hello World!");
    app.MapGet("/about", () => "Contoso was founded in 2000.");
    
    app.Run();
    

    现在,自定义中间件组件添加到了 URL 重写器中间件之前。 自定义中间件组件在 URL 重写器中间件处理请求并重定向请求之前记录请求详细信息。

  2. 再次重启应用,并像以前一样对其进行测试。 这一次, 调试控制台 输出应包含页面 /history 的请求。

    GET / 200
    GET /history 200
    GET /about 200
    

    控制台输出现在会在重定向到 /history 页面之前显示对 /about 页面的请求。

修复状态代码

该应用几乎已准备就绪,但还有一个问题。 控制台输出中的状态代码始终为 200,即使应用重定向了请求也是如此。 /history 请求的状态代码应为 302 重定向。 发生此行为是因为处理中间件组件的顺序存在另一个问题。

自定义中间件组件将详细信息记录到控制台,然后调用 await next() 来传递给下一个中间件组件。 问题是,StatusCode 对象的 Response 属性是在终端中间件组件启动响应后设置的。 让我们更改代码以修复此问题。

  1. 在你添加的委托中,将 Console.WriteLine() 行移动到 await next() 行之后。

    更新后的代码应如下所示:

    app.Use(async (context, next) =>
    {
        await next(); 
        Console.WriteLine($"{context.Request.Method} {context.Request.Path} {context.Response.StatusCode}");
    });
    

    现在,自定义中间件组件将在终端中间件组件设置响应状态代码后记录请求详细信息。

  2. 重启并再次测试 /history 请求。 调试控制台输出现在应显示正确的状态代码。

    GET / 200
    GET /history 302
    GET /about 200