ASP.NET Core编写自定义的中间件(Middleware)

中间件(Middleware)是一种装配在管道里处理请求和响应的组件。ASP.NET Core已提供了丰富的内置中间件,但一些场景中还是需要编写自定义的中间件。

中间件可以通过简单的匿名方法的形式实现:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            var safeIpList = new string[] { "127.0.0.1", "::1", "10.13.0.187" };
            var ip = context.Connection.RemoteIpAddress.ToString();
            if (safeIpList.All(safeIp => safeIp != ip))
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }
            await next();
        });
    }
}

如上所示的代码,只有指定的ip能访问应用程序。否则返回403状态码。

中间件类

中间件通常封装在一个类里面,如下代码将相同的功能通过中间件类的形式实现。

using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace SafeIp
{

    public class RequestSafeIpMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestSafeIpMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task InvokeAsync(HttpContext context)
        {
            var safeIpList = new string[] { "127.0.0.1", "::1", "10.13.0.187" };
            var ip = context.Connection.RemoteIpAddress.ToString();
            if (safeIpList.All(safeIp => safeIp != ip))
            {
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }

            // 调用下一个中间件
            await _next(context);
        }
    }
}

中间件类须满足如下条件:

  • 一个共公的构造函数,接受一个RequestDelegate类型的参数。
  • 一个共公的名为InvokeInvokeAsync,此方法必须:
    • 返回一个Task
    • 接受的第一个参数的类型为HttpContext

构造函数和InvokeInvokeAsync还可以接受其它来自依赖注入的参数。

它通常通过扩展方法暴露在IApplicationBuilder上:

public static class RequestSafeIpMiddlewareExtensions
{
    public static IApplicationBuilder UseSafeIp(
        this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestSafeIpMiddleware>();
    }
}

以下代码从Startup.Configure调用中间件:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRequestCulture();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });
    }
}