chris ~ $ 

software_developer devops_engineer ethical_hacker cheshire_uk

...

  ASP.NET 

dev   web

Minimal Apis

Really great for creating apis for minimal packages.

public static void MapTasksEndpoints(this IEndpointRouteBuilder endpoints)
{
    var group = endpoints.MapGroup("/tasks");

    group.MapGet("", Task<Ok<string[]>> (HttpContext context, [FromServices] IServiceProvider sp) =>
    {   
        sp.GetRequiredService<ILogger<...>>().LogInformation("Getting all tasks");

        var result = TypedResults.Ok<string[]>(["task 1", "task 2"]);
        return Task.FromResult(result);
    });

    group.MapPost("{id:int}", async Task<Results<Ok, ValidationProblem>> (
        HttpContext context, 
        [FromRoute] int id, 
        [FromBody] Task task,
        [FromServices] IServiceProvider sp) =>
    {   
        if (!await task.IsValid())
            return Task.FromResult(TypedResults.ValidationProblem(...));

        Results<Ok, ValidationProblem> result = TypedResults.Ok();
        return result;
    }).RequireAuthorization()
    .WithTags("tasks");
}

Microsoft.FeatureManagement

See: use-feature-flags-dotnet-core

dotnet add package Microsoft.FeatureManagement
dotnet add package Microsoft.FeatureManagement.AspNetCore

Filter on specific claims

public class ClaimsTargettingContextAccessor : ITargetingContextAccessor, IFeatureFilterMetadata
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    
    private const string TargetingContextLookup = "HttpContextAccessor.TargetingContext";
    
    private static readonly List<string> ValidGroups = new()
    { 
        "TenantId"
    };

    public ClaimsTargettingContextAccessor(IHttpContextAccessor httpContextAccessor = null)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public ValueTask<TargetingContext> GetContextAsync()
    {
        // This method checks for cached groups already attached to the HttpContext,
        // If they don't exist then we look for valid claims and adds them to the targetting context.

        if (_httpContextAccessor is null)
            return new ValueTask<TargetingContext>(new TargetingContext());
 
        var httpContext = _httpContextAccessor.HttpContext;
        if (httpContext.Items.TryGetValue(TargetingContextLookup, out object value))
            return new ValueTask<TargetingContext>((TargetingContext)value);

        var groups = httpContext.User.Claims
            .Where(c => ValidGroups.Contains(claim.Type))
            .Select(c => $"{c.Type}:{c.Value}")
            .ToArray();

        TargetingContext targetingContext = new()
        {
            UserId = user.Identity.Name,
            Groups = groups
        };

        httpContext.Items[TargetingContextLookup] = targetingContext;

        return new ValueTask<TargetingContext>(targetingContext);
    }
}
builder.Services.AddFeatureManagement()
                .WithTargeting<ClaimsTargettingContextAccessor>();