ASP.NET Core 实现 cluster-specific API
Intro
前段时间我们一个新的服务里有有一些 API 只想在某个 k8s 集群里可以访问,其他集群里不可以访问,因为关系比较密切又不想拆成两个服务,所以就像实现某些 API 只在特定集群访问,我们的 openapi 文档上也想实现类似的过滤避免别人在 OpenAPI 文档上看到
Sample
新的服务是基于 .NET 10 是实现的,整体比较简单 API 都是通过 Minimal API 的实现的,OpenAPI 通过 ASP.NET 10 里内置的 OpenAPI 支持和 Scalar 来实现 UI
我们会在服务部署到我们 k8s 集群时通过 configmap 注入到服务的环境变量里,可以通过环境变量获取当前 cluster , 我们以 AppSettings__Cluster 环境变量为例,我们来看一下实现的效果
central cluster 的情况, central-0
其他 cluster 的情况, catalog-0
Implement
对于 API 的实现比较简单,可以实现一个 IEndpointFilter 来实现,在不是 central enable 的情况下返回 NotFound
public class CentralClusterOnlyFilter : IEndpointFilter
{
public static readonly CentralClusterOnlyFilter Instance = new();
public static bool Enabled { get; } = Environment.GetEnvironmentVariable("AppSettings__Cluster")
?.Contains("central") == true;
private CentralClusterOnlyFilter() { }
public ValueTask<object?> InvokeAsync(EndpointFilterInvocationContext context, EndpointFilterDelegate next)
{
return Enabled
? next(context)
: ValueTask.FromResult<object?>(Results.NotFound());
}
}对于 OpenAPI 文档,可以通过 AddMetadata 给 Endpoint 添加一个 Metadata 然后再通过 OpenApiOption 中的 ShouldInclude 来将不希望展示的 API 给排除在外,可以定义一个 IEndpointConventionBuilder 的扩展方便使用,实现如下:
public static class EndpointExtension
{
extension<TBuilder>(TBuilder endpoints) where TBuilder : IEndpointConventionBuilder
{
public TBuilder CentralClusterOnly()
{
endpoints
.AddEndpointFilter(CentralClusterOnlyFilter.Instance)
.WithMetadata(CentralClusterOnlyFilter.Instance)
;
return endpoints;
}
}
}针对需要配置的 API endpoint 添加 CentralClusterOnly() 即可
app.MapGet("/central-endpoint", () => Results.Ok())
.CentralClusterOnly();再来自定义一下 OpenAPI 的选项,通过 EndpointMetadata 来判断是否指定了 CentralClusterOnly 如果指定了,则应该仅在 Enable 的情况下包含在 OpenAPI 文档中
builder.Services.AddOpenApi(options =>
{
options.ShouldInclude = d =>
{
var filter = d.ActionDescriptor.EndpointMetadata.OfType<CentralClusterOnlyFilter>().FirstOrDefault();
if (filter is null)
{
return true;
}
return CentralClusterOnlyFilter.Enabled;
};
});More
这个示例是基于 Cluster 来实现的,其实我们也可以根据其他的环境或配置等需要来实现,分享一下希望对大家有帮助