I have many controllers like this:
public class EntityController : Controller
{
private readonly IEntityRepository _entity;
public EntityController(IEntityRepository entity)
{
_entity = entity;
}
[Authorize]
[HttpPut("{id}")]
public async ValueTask<IActionResult> Put(int id, [FromBody] Entity entity)
{
if (entity == null || entity.Id != id) return BadRequest();
var updated = await _entity.Update(entity);
if (updated == null) return NotFound();
return Ok(updated);
}
}
I need to implement entities editing (audit) history.
And, since the method is marked as [Authorize]
, I need to log by which user it was edited.
I'm looking at Audit.NET
, but I didn't find a way to do it.
The Audit.NET EF Provider allows to customize the audit entity before saving it. This has to be done at the startup with a so-called AuditEntity Action: an action that is triggered for each entity being modified.
So, you can make this action retrieve the user name from the current HttpContext
and store it in a UserName
property on your audit entities.
On your asp net startup code, setup a way to obtain the current HttpContext
and configure the action to retrieve the username from the context:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
// Add the HttpContextAccessor if needed.
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Get the service provider to access the http context
var svcProvider = services.BuildServiceProvider();
// Configure Audit.NET
Audit.Core.Configuration.Setup()
.UseEntityFramework(x => x
.AuditTypeNameMapper(typeName => "Audit_" + typeName)
.AuditEntityAction((evt, ent, auditEntity) =>
{
// Get the current HttpContext
var httpContext = svcProvider.GetService<IHttpContextAccessor>().HttpContext;
// Store the identity name on the "UserName" property of the audit entity
((dynamic)auditEntity).UserName = httpContext.User?.Identity.Name;
}));
}
}
This is assuming your audit entities have a common UserName
property.
If your Audit Entities already inherits from an interface or base class including the UserName, you can use the generic AuditEntityAction<T>
instead.
Audit.Core.Configuration.Setup()
.UseEntityFramework(x => x
.AuditTypeNameMapper(typeName => "Audit_" + typeName)
.AuditEntityAction<IUserName>((evt, ent, auditEntity) =>
{
var httpContext = svcProvider.GetService<IHttpContextAccessor>().HttpContext;
auditEntity.UserName = httpContext.User?.Identity.Name;
}));