diff --git a/Webzine.Business/Webzine.Business.csproj b/Webzine.Business/Webzine.Business.csproj index ee626b0..9110639 100644 --- a/Webzine.Business/Webzine.Business.csproj +++ b/Webzine.Business/Webzine.Business.csproj @@ -25,12 +25,7 @@ - - - - - ..\..\..\..\..\..\..\.nuget\packages\microsoft.extensions.logging.abstractions\10.0.5\lib\net10.0\Microsoft.Extensions.Logging.Abstractions.dll - + diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs index 6738804..c68e99e 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/ArtisteController.cs @@ -36,11 +36,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); } /// @@ -60,14 +58,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 { @@ -114,11 +104,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 1ca2c80..67460d2 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/CommentaireController.cs @@ -83,11 +83,6 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers { var commentaire = this.commentaireRepository.Find(model.IdCommentaire); - if (!this.ModelState.IsValid) - { - return this.View(commentaire); - } - if (commentaire != null) { this.commentaireRepository.Delete(commentaire); diff --git a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs index a882a68..560533e 100644 --- a/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs +++ b/Webzine.WebApplication/Areas/Administration/Controllers/DashboardController.cs @@ -5,8 +5,11 @@ using Microsoft.AspNetCore.Mvc; using Webzine.Business.Contracts; using Webzine.Business.Contracts.Dto; +/// +/// Contrôleur pour gérer le tableau de bord de l'administration. +/// [Area("Administration")] -public class DashboardController : Controller // TODO à refaire +public class DashboardController : Controller { private readonly ILogger logger; private readonly IDashboardService dashboardService; 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 12a20a5..e3f7bf2 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 bc3c40a..9980afa 100644 --- a/Webzine.WebApplication/Controllers/ArtisteController.cs +++ b/Webzine.WebApplication/Controllers/ArtisteController.cs @@ -7,7 +7,6 @@ /// /// Contrôleur pour la gestion des artistes dans l'administration du webzine. Ce contrôleur gère les opérations de création, modification, suppression et affichage des artistes dans l'interface d'administration du webzine. Chaque action du contrôleur prépare un ViewModel spécifique pour la vue correspondante, permettant ainsi une séparation claire entre la logique métier et la présentation des données. - /// /// public class ArtisteController : Controller { diff --git a/Webzine.WebApplication/Controllers/TitreController.cs b/Webzine.WebApplication/Controllers/TitreController.cs index 0b824c0..d5ee70b 100644 --- a/Webzine.WebApplication/Controllers/TitreController.cs +++ b/Webzine.WebApplication/Controllers/TitreController.cs @@ -118,7 +118,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 }); } /// @@ -129,12 +129,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) @@ -155,7 +149,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 be8a92c..6ad2b22 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..102c321 100644 --- a/Webzine.WebApplication/Views/Titre/Index.cshtml +++ b/Webzine.WebApplication/Views/Titre/Index.cshtml @@ -60,13 +60,14 @@
- +
- + Editer @@ -88,7 +89,7 @@ class="img-fluid rounded shadow" alt="Jaquette" loading="lazy" - fetchpriority="high" /> + fetchpriority="high"/>
@@ -121,7 +122,7 @@ + required/> @@ -131,10 +132,10 @@
+ rows="3" + class="form-control input-full" + placeholder="Votre commentaire..." + required>
@@ -156,7 +157,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)) { @@ -169,7 +170,7 @@ width="50" height="50" class="rounded-circle me-3 shadow-sm" - alt="avatar" /> + alt="avatar"/>
@comment.Auteur, 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 @@