Files
webzine/Webzine.WebApplication/Filters/ValidationActionFilter.cs

73 lines
2.5 KiB
C#

namespace Webzine.WebApplication.Filters;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
/// <summary>
/// Filtre d'action qui valide automatiquement le ModelState avant l'exécution du contrôleur.
/// Mesure également le temps d'exécution de chaque action (niveau Trace).
/// </summary>
public class ValidationActionFilter : IActionFilter
{
private readonly ILogger<ValidationActionFilter> logger;
/// <summary>
/// Initializes a new instance of the <see cref="ValidationActionFilter"/> class.
/// </summary>
/// <param name="logger">Service de journalisation injecté.</param>
public ValidationActionFilter(ILogger<ValidationActionFilter> logger)
{
this.logger = logger;
}
/// <inheritdoc/>
public void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var erreurs = context.ModelState
.Where(e => e.Value?.Errors.Count > 0)
.Select(e => $"{e.Key}: {string.Join(", ", e.Value!.Errors.Select(err => err.ErrorMessage))}")
.ToList();
this.logger.LogWarning(
"Validation échouée pour {Action} : {Erreurs}",
context.ActionDescriptor.DisplayName,
string.Join(" | ", erreurs));
string actionName = context.RouteData.Values["action"]?.ToString() ?? string.Empty;
// cas spécial: titre details
if (actionName.Equals("Index", StringComparison.OrdinalIgnoreCase))
{
context.Result = new RedirectResult("/");
return;
}
// Récupère le modèle soumis (premier argument de l'action, s'il existe)
object? model = context.ActionArguments.Values.FirstOrDefault();
if (context.Controller is Controller controller)
{
context.Result = new ViewResult
{
ViewName = actionName,
ViewData = new Microsoft.AspNetCore.Mvc.ViewFeatures.ViewDataDictionary(
controller.ViewData)
{
Model = model,
},
};
}
else
{
context.Result = new BadRequestObjectResult(context.ModelState);
}
}
}
/// <inheritdoc/>
public void OnActionExecuted(ActionExecutedContext context)
{
}
}