有没有办法将元数据添加到 ASP.NET Core 中的所有 GET 端点?
Is there a way to add metadata to all GET endpoints in ASP.NET Core?
在下面的语句中,AsBffApiEndpoint()
向所有端点添加了一个属性。然后有一个专门寻找该属性的中间件,如果存在,将检查防伪 header 是否存在。
endpoints.MapControllers().RequireAuthorization().AsBffApiEndpoint();
我需要能够绕过对所有 GET 端点的检查。最重要的是,这是第三方库,因此我无法控制实现。
我试过很多东西都没有成功。最后一次尝试是添加中间件自定义中间件 app.Use(...)
,如果该属性存在,则将其删除。但是这是不可能的,因为元数据列表是 readonly
。然后,我最后的希望是找到一种方法来添加相同的属性 - 到所有 GET - 带有忽略检查的标志 false
。换句话说,所有 AsBffApiEndpoint()
所做的就是用 [BffApi]
属性装饰一个端点。如果像这样使用 [BffApi(false)]
,此属性将忽略防盗器 headers。我知道解决方案很复杂,因为我最终会得到这样的结果。
[BffApi]
[BffApi(false)]
//endpoint definition here
好消息是他们得到了有序的端点元数据 endpoint.Metadata.GetOrderedMetadata<BffApiAttribute>()
。意思是只要 [BffApi(false)]
在列表中优先,我就可以了。
我找到了解决方案,不涉及添加第二个属性,而是扩展构建器。这样我就可以修改端点元数据,此时它是可变的。
public static TBuilder AsBffApiEndpointBypassAntiforgeryOnGET<TBuilder>(this TBuilder builder, string routePrefix) where TBuilder : IEndpointConventionBuilder
{
builder.Add(endpointBuilder =>
{
var getAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(HttpGetAttribute)) as HttpGetAttribute;
var routeAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(RouteAttribute)) as RouteAttribute;
if (getAttribute != null && routeAttribute != null && routeAttribute.Template.StartsWith(routePrefix))
{
endpointBuilder.Metadata.Add(new BffApiAttribute(false));
}
else
{
endpointBuilder.Metadata.Add(new BffApiAttribute(true));
}
});
return builder;
}
然后在Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapBffManagementEndpoints();
endpoints.MapControllers().RequireAuthorization().AsBffApiEndpointBypassAntiforgeryOnGET("api/Foo");
});
更通用的实现如下。
public static IEndpointConventionBuilder AddConditionalMetadata(this IEndpointConventionBuilder builder, Func<EndpointBuilder, bool> evalEndpoint, Action<EndpointBuilder> onEvalTrue, Action<EndpointBuilder> onEvalFalse)
{
builder.Add(endpointBuilder =>
{
if (evalEndpoint.Invoke(endpointBuilder))
{
onEvalTrue.Invoke(endpointBuilder);
}
else
{
onEvalFalse.Invoke(endpointBuilder);
}
});
return builder;
}
通过这种方式,您可以公开要评估的函数和要执行的操作。那么,你的Startup.cs
就会变成这样。
app.UseEndpoints(endpoints =>
{
endpoints.MapBffManagementEndpoints();
endpoints.MapControllers().RequireAuthorization().AddConditionalMetadata(
evalEndpoint: (endpointBuilder) =>
{
var routeAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(RouteAttribute)) as RouteAttribute;
var getAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(HttpGetAttribute)) as HttpGetAttribute;
return getAttribute != null && routeAttribute != null && routeAttribute.Template.StartsWith("api/Foo");
},
onEvalTrue: (endpointBuilder) => { endpointBuilder.Metadata.Add(new BffApiAttribute(false)); },
onEvalFalse: (endpointBuilder) => { endpointBuilder.Metadata.Add(new BffApiAttribute()); });
});
我希望这可以帮助其他人寻找同样的东西。
在下面的语句中,AsBffApiEndpoint()
向所有端点添加了一个属性。然后有一个专门寻找该属性的中间件,如果存在,将检查防伪 header 是否存在。
endpoints.MapControllers().RequireAuthorization().AsBffApiEndpoint();
我需要能够绕过对所有 GET 端点的检查。最重要的是,这是第三方库,因此我无法控制实现。
我试过很多东西都没有成功。最后一次尝试是添加中间件自定义中间件 app.Use(...)
,如果该属性存在,则将其删除。但是这是不可能的,因为元数据列表是 readonly
。然后,我最后的希望是找到一种方法来添加相同的属性 - 到所有 GET - 带有忽略检查的标志 false
。换句话说,所有 AsBffApiEndpoint()
所做的就是用 [BffApi]
属性装饰一个端点。如果像这样使用 [BffApi(false)]
,此属性将忽略防盗器 headers。我知道解决方案很复杂,因为我最终会得到这样的结果。
[BffApi]
[BffApi(false)]
//endpoint definition here
好消息是他们得到了有序的端点元数据 endpoint.Metadata.GetOrderedMetadata<BffApiAttribute>()
。意思是只要 [BffApi(false)]
在列表中优先,我就可以了。
我找到了解决方案,不涉及添加第二个属性,而是扩展构建器。这样我就可以修改端点元数据,此时它是可变的。
public static TBuilder AsBffApiEndpointBypassAntiforgeryOnGET<TBuilder>(this TBuilder builder, string routePrefix) where TBuilder : IEndpointConventionBuilder
{
builder.Add(endpointBuilder =>
{
var getAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(HttpGetAttribute)) as HttpGetAttribute;
var routeAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(RouteAttribute)) as RouteAttribute;
if (getAttribute != null && routeAttribute != null && routeAttribute.Template.StartsWith(routePrefix))
{
endpointBuilder.Metadata.Add(new BffApiAttribute(false));
}
else
{
endpointBuilder.Metadata.Add(new BffApiAttribute(true));
}
});
return builder;
}
然后在Startup.cs
app.UseEndpoints(endpoints =>
{
endpoints.MapBffManagementEndpoints();
endpoints.MapControllers().RequireAuthorization().AsBffApiEndpointBypassAntiforgeryOnGET("api/Foo");
});
更通用的实现如下。
public static IEndpointConventionBuilder AddConditionalMetadata(this IEndpointConventionBuilder builder, Func<EndpointBuilder, bool> evalEndpoint, Action<EndpointBuilder> onEvalTrue, Action<EndpointBuilder> onEvalFalse)
{
builder.Add(endpointBuilder =>
{
if (evalEndpoint.Invoke(endpointBuilder))
{
onEvalTrue.Invoke(endpointBuilder);
}
else
{
onEvalFalse.Invoke(endpointBuilder);
}
});
return builder;
}
通过这种方式,您可以公开要评估的函数和要执行的操作。那么,你的Startup.cs
就会变成这样。
app.UseEndpoints(endpoints =>
{
endpoints.MapBffManagementEndpoints();
endpoints.MapControllers().RequireAuthorization().AddConditionalMetadata(
evalEndpoint: (endpointBuilder) =>
{
var routeAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(RouteAttribute)) as RouteAttribute;
var getAttribute = endpointBuilder.Metadata.FirstOrDefault(m => m.GetType() == typeof(HttpGetAttribute)) as HttpGetAttribute;
return getAttribute != null && routeAttribute != null && routeAttribute.Template.StartsWith("api/Foo");
},
onEvalTrue: (endpointBuilder) => { endpointBuilder.Metadata.Add(new BffApiAttribute(false)); },
onEvalFalse: (endpointBuilder) => { endpointBuilder.Metadata.Add(new BffApiAttribute()); });
});
我希望这可以帮助其他人寻找同样的东西。