At my workplace, I’ve been tasked with refactoring some code in an ASP.NET Core Web API application. In the application, I have multiple request models where some models include properties like UserId, ChartId, or both. For instance, the AcceptEvent model includes all these properties, while other models might only include ChartId.
I have created a service class to retrieve user claims:
public class UserClaimsService : IUserClaimsService
{
public long ProvinceId { get; }
public long UserId { get; }
public long LoginChart { get; }
public UserClaimsService(IHttpContextAccessor httpContextAccessor)
{
ClaimsPrincipal? user = httpContextAccessor.HttpContext.User;
if (user.Identity.IsAuthenticated && user != null)
{
UserId = long.Parse(user.Claims.First(a => a.Type == ClaimTypes.NameIdentifier).Value);
ProvinceId = long.Parse(user.Claims.First(a => a.Type == "ProvinceId").Value);
LoginChart = long.Parse(user.Claims.First(a => a.Type == "ChartId").Value);
}
}
}
public interface IUserClaimsService
{
public long ProvinceId { get; }
public long UserId { get; }
public long LoginChart { get; }
}
This service is registered with AddScoped. However, I am unsure how to dynamically set these optional properties (e.g., ProvinceId, LoginChart) in the request models consistently.
Is using a global filter a good approach for dynamically setting these properties in request models? How can I achieve this, and are there any performance considerations or best practices I should be aware of?
You could use global filters to dynamically set properties in request models. to do that you can follow these below steps:
1)Create a custom action filter that injects the IUserClaimsService
and sets the relevant properties in the request models.
Filters/SetClaimsPropertiesFilter.cs:
using Microsoft.AspNetCore.Mvc.Filters;
using ClaimsApi.Services;
using ClaimsApi.Models;
public class SetClaimsPropertiesFilter : IActionFilter
{
private readonly IUserClaimsService _userClaimsService;
public SetClaimsPropertiesFilter(IUserClaimsService userClaimsService)
{
_userClaimsService = userClaimsService;
}
public void OnActionExecuting(ActionExecutingContext context)
{
foreach (var arg in context.ActionArguments.Values)
{
if (arg is IClaimUserId claimUserId)
{
claimUserId.UserId = _userClaimsService.UserId;
}
if (arg is IClaimChartId claimChartId)
{
claimChartId.ChartId = _userClaimsService.LoginChart;
}
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
}
}
2)Create interfaces that your request models can implement to indicate which properties should be set.
Models/IClaimUserId.cs:
public interface IClaimUserId
{
long UserId { get; set; }
}
Models/IClaimChartId.cs:
public interface IClaimChartId
{
long ChartId { get; set; }
}
3)Implement Interfaces in Request Models:
Models/AcceptEventModel.cs:
public class AcceptEventModel : IClaimUserId, IClaimChartId
{
public long UserId { get; set; }
public long ChartId { get; set; }
}
Models/AnotherRequestModel.cs:
public class AnotherRequestModel : IClaimChartId
{
public long ChartId { get; set; }
}
4)Program.cs:
using ClaimsApi.Filters;
using ClaimsApi.Services;
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers(config =>
{
config.Filters.Add<SetClaimsPropertiesFilter>();
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddScoped<IUserClaimsService, UserClaimsService>();
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<SetClaimsPropertiesFilter>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Use(async (context, next) =>
{
var userClaimsService = context.RequestServices.GetService<IUserClaimsService>();
// This is just for testing purposes; in real scenarios, authentication middleware will set user claims.
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, "1"),
new Claim("ProvinceId", "2"),
new Claim("ChartId", "3")
};
var identity = new ClaimsIdentity(claims, "TestAuthType");
context.User = new ClaimsPrincipal(identity);
await next.Invoke();
});
app.Run();