【问题标题】:Is there a way to add metadata to all GET endpoints in ASP.NET Core?有没有办法将元数据添加到 ASP.NET Core 中的所有 GET 端点?
【发布时间】:2022-01-19 18:01:41
【问题描述】:

在以下语句中,AsBffApiEndpoint() 将属性添加到所有端点。然后有一个中间件专门寻找该属性,如果存在将检查是否存在防伪标头。

endpoints.MapControllers().RequireAuthorization().AsBffApiEndpoint();

我需要能够绕过对所有 GET 端点的检查。最重要的是,这是第三方库,因此我无法控制实现。

我尝试了很多事情都没有成功。最后一次尝试是添加一个中间件自定义中间件app.Use(...),如果该属性存在,则将其删除。但是这是不可能的,因为元数据列表是readonly。然后,我最后的希望是找到一种方法将相同的属性添加到所有 GET 中,并带有一个忽略检查的标志 false。换句话说,AsBffApiEndpoint() 所做的只是用[BffApi] 属性装饰一个端点。如果像 [BffApi(false)] 这样使用,此属性会忽略 antiforery 标头。我知道解决方案很老套,因为我最终会得到这样的结果。

[BffApi]
[BffApi(false)]
//endpoint definition here

好消息是他们获得了订购的端点元数据endpoint.Metadata.GetOrderedMetadata<BffApiAttribute>()。这意味着只要[BffApi(false)] 在列表中占据优先级,我就应该很好。

【问题讨论】:

标签: c# asp.net-core asp.net-core-middleware duende


【解决方案1】:

我找到了解决方案,没有涉及添加第二个属性,而是扩展了构建器。这样我就可以修改端点元数据,此时它是可变的。

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()); });
});

我希望这可以帮助其他人寻找相同的东西。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-09
    • 1970-01-01
    • 2020-09-28
    • 2017-06-17
    • 2021-05-19
    相关资源
    最近更新 更多