From 8acafbfe496b593162768cfa18299a0143d862ab Mon Sep 17 00:00:00 2001 From: mirage <119869686+ClementBobin@users.noreply.github.com> Date: Thu, 2 Apr 2026 15:57:52 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20simplifier=20les=20actions=20du=20c?= =?UTF-8?q?ontr=C3=B4leur=20en=20supprimant=20les=20contr=C3=B4les=20Model?= =?UTF-8?q?State=20redondants=20et=20en=20am=C3=A9liorant=20la=20r=C3=A9cu?= =?UTF-8?q?p=C3=A9ration=20des=20donn=C3=A9es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ArtisteController.cs | 19 +---- .../Controllers/CommentaireController.cs | 3 + .../Controllers/DashboardController.cs | 2 +- .../Controllers/StyleController.cs | 11 --- .../Controllers/TitreController.cs | 18 +---- .../Controllers/AccueilController.cs | 1 + .../Controllers/ApiController.cs | 5 +- .../Controllers/ArtisteController.cs | 4 + .../Controllers/RechercheController.cs | 9 +++ .../Controllers/TitreController.cs | 10 +-- .../Filters/GlobalExceptionFilter.cs | 42 +++++++++++ .../Filters/ValidationActionFilter.cs | 73 +++++++++++++++++++ Webzine.WebApplication/Program.cs | 11 ++- .../Views/Titre/Index.cshtml | 5 +- .../Views/Titre/Style.cshtml | 4 +- Webzine.WebApplication/nlog.config | 2 +- 16 files changed, 158 insertions(+), 61 deletions(-) create mode 100644 Webzine.WebApplication/Filters/GlobalExceptionFilter.cs create mode 100644 Webzine.WebApplication/Filters/ValidationActionFilter.cs diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs index e6ad7e5..89cbbfb 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs @@ -37,11 +37,9 @@ public class ArtisteController : Controller /// Redirection. public IActionResult Index() { - IEnumerable artistes = this.artisteRepository.FindAll(); + IEnumerable artistes = this.artisteRepository.FindAll().OrderBy(t => t.Nom); - var artistes_ordre = artistes.OrderBy(t => t.Nom).ToList(); - - return this.View(artistes_ordre); + return this.View(artistes); } /// @@ -61,14 +59,6 @@ public class ArtisteController : Controller [HttpPost] public IActionResult Create(ArtisteCreateViewModel model) { - // vérifier si les données sont corrects. - if (!this.ModelState.IsValid) - { - // Passer model en paramètre afin que - // l'utilisateur conserve sa saissie. - return this.View(model); - } - // Créer un objet Artiste avecc les paramètres. var artiste = new Artiste { @@ -115,11 +105,6 @@ public class ArtisteController : Controller Biographie = model.Biographie, }; - if (!this.ModelState.IsValid) - { - return this.View(artiste); - } - this.artisteRepository.Update(artiste); return this.RedirectToAction("Index"); diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs index b954e14..cca385a 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs @@ -5,6 +5,9 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers using Webzine.Repository.Contracts; using Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire; + /// + /// + /// [Area("Administration")] public class CommentaireController : Controller { diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs index 97f1721..213498f 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs @@ -31,7 +31,7 @@ public class DashboardController : Controller /// La vue Index du tableau de bord. public IActionResult Index() { - DashboardDTO data = dashboardService.GetDashboardData(); + DashboardDTO data = this.dashboardService.GetDashboardData(); return this.View(data); } diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/StyleController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/StyleController.cs index 1283fd0..578cf4d 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/StyleController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/StyleController.cs @@ -63,11 +63,6 @@ public class StyleController : Controller [HttpPost] public IActionResult Create(StyleCreateViewModel model) { - if (!this.ModelState.IsValid) - { - return this.View(model); - } - var style = new Style { Libelle = model.Libelle, @@ -124,7 +119,6 @@ public class StyleController : Controller /// /// L'identifiant du style a editer. /// La vue d'edition ou une redirection vers l'index si le style n'existe pas. - [HttpGet] public IActionResult Edit(int id) { var style = this.styleRepository.Find(id); @@ -151,11 +145,6 @@ public class StyleController : Controller [HttpPost] public IActionResult Edit(StyleEditViewModel model) { - if (!this.ModelState.IsValid) - { - return this.View(model); - } - var style = this.styleRepository.Find(model.IdStyle); if (style == null) { diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs index f808dda..d6952f2 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/TitreController.cs @@ -95,13 +95,8 @@ public class TitreController : Controller [HttpPost] public IActionResult Create(TitreAdminDTO model) { - if (this.ModelState.IsValid) - { - this.titreAdminService.CreerTitre(model); - return this.RedirectToAction("Index"); - } - - return this.View(model); + this.titreAdminService.CreerTitre(model); + return this.RedirectToAction("Index"); } /// @@ -152,13 +147,8 @@ public class TitreController : Controller [HttpPost] public IActionResult Edit(TitreAdminDTO model) { - if (this.ModelState.IsValid) - { - this.titreAdminService.ModifierTitre(model); - return this.RedirectToAction("Index"); - } - - return this.View(model); + this.titreAdminService.ModifierTitre(model); + return this.RedirectToAction("Index"); } /// diff --git a/Webzine.WebApplication/Controllers/AccueilController.cs b/Webzine.WebApplication/Controllers/AccueilController.cs index 4d15d99..0efbd59 100644 --- a/Webzine.WebApplication/Controllers/AccueilController.cs +++ b/Webzine.WebApplication/Controllers/AccueilController.cs @@ -21,6 +21,7 @@ /// /// Service de journalisation injecté pour enregistrer les événements et les erreurs. /// Service d'injection de configuration pour accéder aux paramètres de l'application. + /// public AccueilController( ILogger logger, IConfiguration configuration, diff --git a/Webzine.WebApplication/Controllers/ApiController.cs b/Webzine.WebApplication/Controllers/ApiController.cs index 0327185..5f045f7 100644 --- a/Webzine.WebApplication/Controllers/ApiController.cs +++ b/Webzine.WebApplication/Controllers/ApiController.cs @@ -2,12 +2,14 @@ namespace Webzine.WebApplication.Controllers; using Microsoft.AspNetCore.Mvc; +/// +/// Controller de version de l'API. +/// public class ApiController : ControllerBase { private readonly ILogger logger; /// - /// Initializes a new instance of the class. /// Initialise une nouvelle instance de la classe . /// /// Service de journalisation injecté pour enregistrer les événements et les erreurs. @@ -21,7 +23,6 @@ public class ApiController : ControllerBase /// Endpoint de test pour vérifier que l'API fonctionne correctement. Retourne un objet JSON contenant le nom et la version de l'application. /// /// Un objet JSON avec les propriétés "nom" et "version". - [HttpGet] public IActionResult Version() { this.logger.LogInformation("Get Version was called"); diff --git a/Webzine.WebApplication/Controllers/ArtisteController.cs b/Webzine.WebApplication/Controllers/ArtisteController.cs index ef5781b..f3cb9ba 100644 --- a/Webzine.WebApplication/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Controllers/ArtisteController.cs @@ -5,6 +5,9 @@ using Webzine.Repository.Contracts; using Webzine.WebApplication.ViewModels.Artiste; + /// + /// + /// public class ArtisteController : Controller { // Injection du logger via le constructeur @@ -16,6 +19,7 @@ /// Initialise une nouvelle instance du . avec un service de journalisation injecté. /// /// Service de journalisation injecté pour enregistrer les événements et les erreurs. + /// public ArtisteController( ILogger logger, IArtisteRepository artisteRepository) diff --git a/Webzine.WebApplication/Controllers/RechercheController.cs b/Webzine.WebApplication/Controllers/RechercheController.cs index 2070c8c..27027c0 100644 --- a/Webzine.WebApplication/Controllers/RechercheController.cs +++ b/Webzine.WebApplication/Controllers/RechercheController.cs @@ -9,12 +9,21 @@ namespace Webzine.WebApplication.Controllers using Webzine.Repository.Contracts; using Webzine.WebApplication.ViewModels.Recherche; + /// + /// + /// public class RechercheController : Controller { private readonly ILogger logger; private readonly ITitreRepository titreRepository; private readonly IArtisteRepository artisteRepository; + /// + /// Initialise une nouvelle instance de la classe . + /// + /// + /// + /// public RechercheController( ILogger logger, ITitreRepository titreRepository, diff --git a/Webzine.WebApplication/Controllers/TitreController.cs b/Webzine.WebApplication/Controllers/TitreController.cs index 0dbad27..73746b7 100644 --- a/Webzine.WebApplication/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Controllers/TitreController.cs @@ -119,7 +119,7 @@ namespace Webzine.WebApplication.Controllers this.titreRepository.IncrementNbLikes(titre); } - return this.RedirectToAction("Details", new { id = model.IdTitre }); + return this.RedirectToAction("Index", new { id = model.IdTitre }); } /// @@ -130,12 +130,6 @@ namespace Webzine.WebApplication.Controllers [HttpPost] public IActionResult Comment(TitreComment model) { - if (!this.ModelState.IsValid) - { - this.logger.LogWarning("Echec de validation du modele de commentaire pour le titre ID {Id}.", model.IdTitre); - return this.RedirectToAction("Details", new { id = model.IdTitre }); - } - var titre = this.titreRepository.Find(model.IdTitre); if (titre == null) @@ -156,7 +150,7 @@ namespace Webzine.WebApplication.Controllers this.logger.LogInformation("Commentaire ajoute avec succes au titre ID {Id}.", model.IdTitre); - return this.RedirectToAction("Details", new { id = model.IdTitre }); + return this.RedirectToAction("Index", new { id = model.IdTitre }); } private static TitreStyleItem MapTitreItem(Titre titre) diff --git a/Webzine.WebApplication/Filters/GlobalExceptionFilter.cs b/Webzine.WebApplication/Filters/GlobalExceptionFilter.cs new file mode 100644 index 0000000..15f9b73 --- /dev/null +++ b/Webzine.WebApplication/Filters/GlobalExceptionFilter.cs @@ -0,0 +1,42 @@ +namespace Webzine.WebApplication.Filters; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +/// +/// Filtre d'exception global qui intercepte toute exception non gérée et la journalise automatiquement. +/// +public class GlobalExceptionFilter : IExceptionFilter +{ + private readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// Service de journalisation injecté. + public GlobalExceptionFilter(ILogger logger) + { + this.logger = logger; + } + + /// + public void OnException(ExceptionContext context) + { + this.logger.LogError( + context.Exception, + "Erreur non gérée dans {Action} : {Message}", + context.ActionDescriptor.DisplayName, + context.Exception.Message); + + context.Result = new ObjectResult(new + { + erreur = "Une erreur inattendue est survenue.", + detail = context.Exception.Message, + }) + { + StatusCode = StatusCodes.Status500InternalServerError, + }; + + context.ExceptionHandled = true; + } +} \ No newline at end of file diff --git a/Webzine.WebApplication/Filters/ValidationActionFilter.cs b/Webzine.WebApplication/Filters/ValidationActionFilter.cs new file mode 100644 index 0000000..94d0efa --- /dev/null +++ b/Webzine.WebApplication/Filters/ValidationActionFilter.cs @@ -0,0 +1,73 @@ +namespace Webzine.WebApplication.Filters; + +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +/// +/// 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). +/// +public class ValidationActionFilter : IActionFilter +{ + private readonly ILogger logger; + + /// + /// Initializes a new instance of the class. + /// + /// Service de journalisation injecté. + public ValidationActionFilter(ILogger logger) + { + this.logger = logger; + } + + /// + 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); + } + } + } + + /// + public void OnActionExecuted(ActionExecutedContext context) + { + } +} \ No newline at end of file diff --git a/Webzine.WebApplication/Program.cs b/Webzine.WebApplication/Program.cs index 935e2da..72fc4ba 100644 --- a/Webzine.WebApplication/Program.cs +++ b/Webzine.WebApplication/Program.cs @@ -15,6 +15,7 @@ using Webzine.Repository; using Webzine.Repository.Contracts; using Webzine.WebApplication.Configuration; using Webzine.WebApplication.Extensions; +using Webzine.WebApplication.Filters; using Webzine.WebApplication.Interceptors; // Initiation du logger NLog pour la classe courante afin de pouvoir l'utiliser pour logger des messages d'information, d'erreur, etc avant la construction de l'application. @@ -27,8 +28,11 @@ try // Ajoute les services necessaires pour permettre l'utilisation des // controllers avec des vues. - builder.Services.AddControllersWithViews() - + builder.Services.AddControllersWithViews(options => + { + options.Filters.Add(); + options.Filters.Add(); + }) // Ajoute la compilation des vues lors de l'execution de l'application. // Cela nous evite de recompiler l'application a chaque modification de vue. // Necessite le package Nuget Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation. @@ -87,6 +91,9 @@ try builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddScoped(); + // https://learn.microsoft.com/fr-fr/aspnet/core/performance/response-compression?view=aspnetcore-10.0#configuration // Ajoute le service de compression des réponses HTTP pour réduire la taille des données envoyées au client et améliorer les performances de l'application. builder.Services.AddResponseCompression(); diff --git a/Webzine.WebApplication/Views/Titre/Index.cshtml b/Webzine.WebApplication/Views/Titre/Index.cshtml index d668fd9..215c08a 100644 --- a/Webzine.WebApplication/Views/Titre/Index.cshtml +++ b/Webzine.WebApplication/Views/Titre/Index.cshtml @@ -133,8 +133,7 @@ + placeholder="Votre commentaire..."> @@ -156,7 +155,7 @@

Commentaires

- @if (Model.Details.Commentaires != null && Model.Details.Commentaires.Any()) + @if (Model.Details.Commentaires.Any()) { foreach (var comment in Model.Details.Commentaires.OrderByDescending(c => c.DateCreation)) { diff --git a/Webzine.WebApplication/Views/Titre/Style.cshtml b/Webzine.WebApplication/Views/Titre/Style.cshtml index 1c50f0c..6970347 100644 --- a/Webzine.WebApplication/Views/Titre/Style.cshtml +++ b/Webzine.WebApplication/Views/Titre/Style.cshtml @@ -26,7 +26,7 @@
- @titre.Libelle @@ -41,7 +41,7 @@ @titre.ArtisteNom - - @titre.Libelle diff --git a/Webzine.WebApplication/nlog.config b/Webzine.WebApplication/nlog.config index 1b64237..2fc8c32 100644 --- a/Webzine.WebApplication/nlog.config +++ b/Webzine.WebApplication/nlog.config @@ -22,7 +22,7 @@ layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|${aspnet-request-url:whenEmpty=NoRequest}" /> -