ASP.NET 核心 2.1 中间件 returns 404

ASP.NET Core 2.1 Middleware returns 404

我决定为 ASP.NET API 核心 2.1 制作自定义中间件。

public class AuthorizeMiddleware
{
    private readonly RequestDelegate _next;
    private readonly AuthorizeOptions _options;

    public AuthorizeMiddleware(RequestDelegate next, AuthorizeOptions options)
    {
        _next = next;
        _options = options;
    }


    public async Task Invoke(HttpContext context)
    {
        bool hasRole = false;
        if (hasRole)
        {
            await context.Response.WriteAsync($"Not authorized, you need role: {_options.Role}");
        }
        else
        {
            await _next.Invoke(context);
        }

    }

}        
public struct AuthorizeOptions
{
    public AuthorizeOptions(string role)
    {
        Role = role;
    }
    public string Role { get; set; }
} 

当我尝试在我的 Application.cs

中使用这个中间件时
 public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {

        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseHsts();
            app.UseHttpsRedirection();
        }

        app.UseRouter(AuthenticatedRoutes(app));

        app.UseMvc();
    }

    private IRouter AuthenticatedRoutes(IApplicationBuilder applicationBuilder)
    {

        IRouteBuilder builder = new RouteBuilder(applicationBuilder);

        builder.MapMiddlewareGet("/api/values", appBuilder =>
        {
            appBuilder.UseMiddleware<AuthorizeMiddleware>(new AuthorizeOptions("User"));
            appBuilder.UseMvc();
        });

        return builder.Build();
    }

这很好用,但是当我删除 appBuilder.UseMvc();从 MapMiddlewareGet 和具体路由我的函数调用 returns 404.

我试图将 appRouter 放在 app.useMvc() 之上。没有成功,当 _next.Invoke() 被调用时,我的中间件 next 函数仍然返回 404。

那么,为什么每当我在 appBuilder 中调用 useMvc() 时它都会工作,我是否在做一些被认为是不好的做法,为什么我必须在 MapMiddlewareGet() 中使用 app.useMvc()?

你的 AuthenticatedRoutes() 试图做的是用中间件构建一个路由器,因此我们可以使用最终返回的 IRouter 作为 RouterMiddleware 来处理请求。 但是,一旦已经有匹配的处理程序RouterMiddleware 将永远不会继续路由。因此,它不会 "dispatch" 自动从一个 RouterMiddlware 向另一个 RouterMiddleware 请求。

让我们检查一下您的代码:

app.UseRouter(AuthenticatedRoutes(app));

如您所知,这里的方法app.UseRouter()是一个扩展方法,它只是使用RouterMiddlware。所以第一个问题是:路由器中间件是如何工作的?让我们看一下源代码:

public class RouterMiddleware
{
    private readonly IRouter _router;

    // ...

    public async Task Invoke(HttpContext httpContext)
    {
        var context = new RouteContext(httpContext);
        context.RouteData.Routers.Add(_router);
        await _router.RouteAsync(context);
        if (context.Handler == null){
            _logger.RequestDidNotMatchRoutes();
            await _next.Invoke(httpContext);
        } else {
            httpContext.Features[typeof(IRoutingFeature)] = new RoutingFeature(){
                RouteData = context.RouteData,
            };
            await context.Handler(context.HttpContext);
        }
    }
}

正如您在此处看到的,RouterMiddleware 路由针对上下文并检查是否有 RouterHandler 匹配:

  1. 如果有none,什么也不做,然后将请求分派给下一个中间件
  2. 否则,使用 RouterHandler 处理请求。 请注意,它永远不会将请求分派给下一个中间件

让我们回顾一下您的路由器和 RouterHandler 的工作原理:

private IRouter AuthenticatedRoutes(IApplicationBuilder applicationBuilder)
{

    IRouteBuilder builder = new RouteBuilder(applicationBuilder);

    builder.MapMiddlewareGet("/api/values", appBuilder =>
    {
        appBuilder.UseMiddleware<AuthorizeMiddleware>(new AuthorizeOptions("User"));
        appBuilder.UseMvc();
    });

    return builder.Build();
}

看到了吗?您的路由将检查 HTTP 方法是否为 HttpGet 以及 url 是否可以匹配 /api/values:

  1. 如果是,说明路由匹配,将调用特定的RouterHandler处理请求。

    • RouterHandler会先调用AuthorizeMiddleware的中间件
    • 如果您添加 appBuilder.UseMvc();,它将调用匹配的操作
    • 如果没有appBuilder.UseMvc();,它将终止进一步的处理,最终产生404响应。
  2. 如果没有,说明这里的路由器不匹配,什么都不做,然后把请求派发给下一个中间件。