Есть ли способ добавить метаданные ко всем конечным точкам GET в ASP.NET Core?
В следующем операторе добавляется атрибут ко всем конечным точкам. Затем есть промежуточное ПО, которое специально ищет этот атрибут и, если он присутствует, проверяет наличие заголовка защиты от подделки.
endpoints.MapControllers().RequireAuthorization().AsBffApiEndpoint();
Мне нужно иметь возможность обойти эту проверку на всех конечных точках GET. Самое главное, что это сторонняя библиотека, поэтому я не могу контролировать ее реализацию.
Я пробовал много вещей без успеха. Последней попыткой было добавить пользовательское промежуточное ПО промежуточного слоя.
app.Use(...)
и если атрибут присутствовал, то удалить его. Однако это невозможно, поскольку список метаданных
readonly
. Тогда моя последняя надежда - найти способ добавить один и тот же атрибут - ко всем GET- с флагом
false
который игнорирует проверку. Другими словами, все
AsBffApiEndpoint()
украшает конечную точку
[BffApi]
атрибут. Этот атрибут игнорирует заголовки защиты от форери, если используется таким образом. Я знаю, что решение хакерское, потому что в итоге я получу что-то вроде этого.
[BffApi]
[BffApi(false)]
//endpoint definition here
Хорошей новостью является то, что они получают упорядоченные метаданные конечной точки.
endpoint.Metadata.GetOrderedMetadata<BffApiAttribute>()
. Значение до тех пор, пока
[BffApi(false)]
имеет приоритет в списке, я должен быть хорошим.
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;
}
Затем в
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()); });
});
Я надеюсь, что это может помочь кому-то еще, кто ищет то же самое.