ASP.NET Core实现未匹配路由请求的代理转发问题
你碰到的核心问题是中间件执行顺序——MapWhen默认是在路由匹配中间件之前运行的,这时候请求还没经过路由匹配,响应状态码自然不会是404,所以你的判断条件根本不会触发。下面给你两个简洁的方案,既能用aspnetcore.Proxy简化代理操作,又能保证只转发未匹配现有路由的请求:
方案一:用MapFallback(推荐,ASP.NET Core 3.0+支持)
MapFallback是ASP.NET Core专门用来处理未匹配到任何端点的请求的,它会在所有路由匹配完成后才执行,完美契合你的需求。直接在UseEndpoints里添加这个规则就行:
app.UseRouting(); app.UseEndpoints(endpoints => { // 先注册你的现有路由(比如控制器路由) endpoints.MapControllers(); // 当没有任何路由匹配时,执行代理 endpoints.MapFallback(async context => { var proxyOptions = new ProxyOptions { Scheme = "http", Host = Configuration.GetValue<string>("APIAddress"), Port = Configuration.GetValue<string>("APIPort") }; await context.RunProxy(proxyOptions); }); });
这个方案不需要手动判断404,MapFallback本身就只会在路由完全匹配失败时触发,而且依然能借助aspnetcore.Proxy帮你处理请求转发的所有细节(比如请求头、响应头、内容流的传递),完全不用自己写繁琐的逻辑。
方案二:自定义中间件,在端点执行后检查404
如果你用的是更早版本的ASP.NET Core,或者需要更灵活的控制,可以在路由和端点执行完成后,添加一个中间件来检查响应状态码:
// 先注册路由和端点中间件,这一步必须在代理逻辑之前 app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); // 你的现有路由 }); // 注册代理中间件,在端点执行完成后再判断 app.Use(async (context, next) => { // 先让前面的中间件(包括路由匹配、端点执行)处理请求 await next(); // 只有当响应是404,且还没开始发送响应内容时,才执行代理 if (context.Response.StatusCode == StatusCodes.Status404NotFound && !context.Response.HasStarted) { var proxyOptions = new ProxyOptions { Scheme = "http", Host = Configuration.GetValue<string>("APIAddress"), Port = Configuration.GetValue<string>("APIPort") }; await context.RunProxy(proxyOptions); } });
这里的关键是await next()——它会先让路由匹配和端点执行的逻辑跑完,之后再检查响应状态。加上!context.Response.HasStarted是为了避免在响应已经开始发送后再尝试代理,防止出现异常。
为什么原来的MapWhen不行?
ASP.NET Core的中间件是按注册顺序执行的,你之前的MapWhen是在UseRouting和UseEndpoints之前注册的,这时候请求还没经过路由匹配,context.Response.StatusCode还是默认的200,所以你的判断条件永远不会成立。调整顺序后,就能确保代理逻辑在路由匹配完成后才触发。
内容的提问来源于stack exchange,提问作者Jarry

