我有一个Angular应用程序和许多API,所有这些都通过Yarp反向代理公开。
访问该应用程序的位置https://example.com,以及https://example.com/api/name-of-api.
我的问题是,应用程序的catch-all地址也会选择任何无效的API并路由到应用程序。
我如何设置我的路线https://example.com/api/{*any}将返回404,除非API有更具体的路由?
这就是现有配置的样子:
[
{
"RouteId": "app",
"Match": { "Path": "/{**any}" },
"ClusterId": "app",
"Transforms": []
},
{
"RouteId": "first-api",
"Match": { "Path": "/api/first-api/{*any}" },
"ClusterId": "first-api",
"Transforms": []
}
]
根据文档,您可以使用中间件来实现这一点:
如果中间件检查了一个请求并确定不应该代理它,它可能会生成自己的响应并将控制权返回给服务器,而无需调用next((。
然而,示例中的CheckAllowedRequest
函数并没有提供太多值。下面是一个如何使其工作的例子。
假设您的路由具有RouteId";应用程序";是您想要返回404的路由,您可以使用MetaData字段和您想要的任何键/值对更新配置。对于这个例子,我们将使用";UnsuccessfulResponseStatusCode;key和值"0";404〃:
{
"ReverseProxy": {
"Routes": {
"app": {
"ClusterId": "app",
"Match": {
"Path": "/{**any}"
},
"MetaData": {
"UnsuccessfulResponseStatusCode": "404"
}
},
"first-api": {
"ClusterId": "first-api",
"Match": {
"Path": "/api/first-api/{*any}"
}
}
},
"Clusters": {
"app": {
// Config removed for brevity.
},
"first-api": {
// Config removed for brevity.
}
}
}
}
接下来,您应该将中间件的逻辑封装在一个类中:
namespace SomeNamespace
{
public class SomeMiddleware
{
private readonly RequestDelegate _next;
public SomeMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// Assign your nullable meta data from the config for the
// current requested route to a variable
var metaData = context.GetReverseProxyFeature().Route.Config.Metadata;
// If metaData is not null and a string value, this includes null
// and whitespace, exists for the key "UnsuccessfulResponseStatusCode",
// then short circuit the middleware
if (metaData?.TryGetValue("UnsuccessfulResponseStatusCode"),
out var unsuccessfulResponseStatusCode) ?? false)
{
// Adding a switch case here allows our
// UnsuccessfulResponseStatusCode key to be robust enough to
// handle multiple different unsuccessful response status
// codes if you have different cases for different routes.
switch (unsuccessfulResponseStatusCode)
{
case "404":
context.Response.StatusCode =
StatusCodes.Status404NotFound;
break;
case "500":
default:
context.Response.StatusCode =
StatusCodes.Status500InternalServerError;
break;
}
}
// Otherwise, invoke the next middleware delegate
else
{
await _next(context);
}
}
}
}
最后,我们可以向您的代理管道注册此中间件:
app.MapReverseProxy(proxyPipeline =>
{
// I would register this above most or all of the the other middleware
// to avoid unnecessary processing when attempting to short circuit
// routes you don't want to proxy. This really depends on what other
// middleware you have and what it does. Just be careful here.
proxyPipeline.UseMiddleware<SomeMiddleware>();
});