我决定为 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; }
}
当我尝试在我的应用程序中使用此中间件时.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();
}
这工作得很好,但是当我从MapMiddlewareGet中删除appBuilder.UseMvc((和我的函数调用返回404的特定路由时。
我试图将appRouter放在app.useMvc((之上。没有成功,我的中间件下一个函数在_next时仍然返回 404。调用了 Invoke((。
那么为什么每当我在appBuilder中调用useMvc((时它都会起作用,我是否在做一些被认为是不好的做法,为什么我必须在MapMiddlewareGet((中app.useMvc((?
您的AuthenticatedRoutes()
尝试做的是构建带有中间件的路由器,因此我们可以使用最终返回的 IRouter 作为处理请求的RouterMiddleware
。但是,一旦已经匹配了处理程序,RouterMiddleware
将永远不会继续路由。因此,它不会自动将请求从一个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
:
- 如果没有,则不执行任何操作,然后将请求调度到下一个中间件。
- 否则,使用
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
:
如果是这样,则表示路由匹配,并且将调用特定
RouterHandler
来处理请求。RouterHandler
将首先调用AuthorizeMiddleware
的中间件- 如果添加
appBuilder.UseMvc();
,它将调用匹配的操作 - 如果没有
appBuilder.UseMvc();
,它将终止进一步的进程,并最终产生404
响应。
如果不是,则表示此处的路由器不匹配,并且将不执行任何操作,然后将请求分派给下一个中间件。