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
匹配:
- 如果有none,什么也不做,然后将请求分派给下一个中间件。
- 否则,使用
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
响应。
如果没有,说明这里的路由器不匹配,什么都不做,然后把请求派发给下一个中间件。
我决定为 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
匹配:
- 如果有none,什么也不做,然后将请求分派给下一个中间件。
- 否则,使用
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
响应。
如果没有,说明这里的路由器不匹配,什么都不做,然后把请求派发给下一个中间件。