refactor: simplifier les actions du contrôleur en supprimant les contrôles ModelState redondants et en améliorant la récupération des données
This commit is contained in:
@@ -37,11 +37,9 @@ public class ArtisteController : Controller
|
||||
/// <returns>Redirection.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
IEnumerable<Artiste> artistes = this.artisteRepository.FindAll();
|
||||
IEnumerable<Artiste> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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");
|
||||
|
||||
@@ -5,6 +5,9 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Commentaire;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
[Area("Administration")]
|
||||
public class CommentaireController : Controller
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@ public class DashboardController : Controller
|
||||
/// <returns>La vue Index du tableau de bord.</returns>
|
||||
public IActionResult Index()
|
||||
{
|
||||
DashboardDTO data = dashboardService.GetDashboardData();
|
||||
DashboardDTO data = this.dashboardService.GetDashboardData();
|
||||
|
||||
return this.View(data);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="id">L'identifiant du style a editer.</param>
|
||||
/// <returns>La vue d'edition ou une redirection vers l'index si le style n'existe pas.</returns>
|
||||
[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)
|
||||
{
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
/// <param name="configuration">Service d'injection de configuration pour accéder aux paramètres de l'application.</param>
|
||||
/// <param name="titreRepository"></param>
|
||||
public AccueilController(
|
||||
ILogger<AccueilController> logger,
|
||||
IConfiguration configuration,
|
||||
|
||||
@@ -2,12 +2,14 @@ namespace Webzine.WebApplication.Controllers;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
/// <summary>
|
||||
/// Controller de version de l'API.
|
||||
/// </summary>
|
||||
public class ApiController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<ApiController> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ApiController"/> class.
|
||||
/// Initialise une nouvelle instance de la classe <see cref="ApiController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
/// <returns>Un objet JSON avec les propriétés "nom" et "version".</returns>
|
||||
[HttpGet]
|
||||
public IActionResult Version()
|
||||
{
|
||||
this.logger.LogInformation("Get Version was called");
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Artiste;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
// Injection du logger via le constructeur
|
||||
@@ -16,6 +19,7 @@
|
||||
/// Initialise une nouvelle instance du <see cref="ArtisteController"/>. avec un service de journalisation injecté.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté pour enregistrer les événements et les erreurs.</param>
|
||||
/// <param name="artisteRepository"></param>
|
||||
public ArtisteController(
|
||||
ILogger<ArtisteController> logger,
|
||||
IArtisteRepository artisteRepository)
|
||||
|
||||
@@ -9,12 +9,21 @@ namespace Webzine.WebApplication.Controllers
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.ViewModels.Recherche;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class RechercheController : Controller
|
||||
{
|
||||
private readonly ILogger<RechercheController> logger;
|
||||
private readonly ITitreRepository titreRepository;
|
||||
private readonly IArtisteRepository artisteRepository;
|
||||
|
||||
/// <summary>
|
||||
/// Initialise une nouvelle instance de la classe <see cref="RechercheController"/>.
|
||||
/// </summary>
|
||||
/// <param name="logger"></param>
|
||||
/// <param name="titreRepository"></param>
|
||||
/// <param name="artisteRepository"></param>
|
||||
public RechercheController(
|
||||
ILogger<RechercheController> logger,
|
||||
ITitreRepository titreRepository,
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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)
|
||||
|
||||
42
Webzine.WebApplication/Filters/GlobalExceptionFilter.cs
Normal file
42
Webzine.WebApplication/Filters/GlobalExceptionFilter.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace Webzine.WebApplication.Filters;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
/// <summary>
|
||||
/// Filtre d'exception global qui intercepte toute exception non gérée et la journalise automatiquement.
|
||||
/// </summary>
|
||||
public class GlobalExceptionFilter : IExceptionFilter
|
||||
{
|
||||
private readonly ILogger<GlobalExceptionFilter> logger;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="GlobalExceptionFilter"/> class.
|
||||
/// </summary>
|
||||
/// <param name="logger">Service de journalisation injecté.</param>
|
||||
public GlobalExceptionFilter(ILogger<GlobalExceptionFilter> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
73
Webzine.WebApplication/Filters/ValidationActionFilter.cs
Normal file
73
Webzine.WebApplication/Filters/ValidationActionFilter.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
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)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -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<ValidationActionFilter>();
|
||||
options.Filters.Add<GlobalExceptionFilter>();
|
||||
})
|
||||
// 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<IDashboardService, DashboardService>();
|
||||
builder.Services.AddScoped<ITitreAdminService, TitreAdminService>();
|
||||
|
||||
builder.Services.AddScoped<ValidationActionFilter>();
|
||||
builder.Services.AddScoped<GlobalExceptionFilter>();
|
||||
|
||||
// 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();
|
||||
|
||||
@@ -133,8 +133,7 @@
|
||||
<textarea name="Contenu"
|
||||
rows="3"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre commentaire..."
|
||||
required></textarea>
|
||||
placeholder="Votre commentaire..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,7 +155,7 @@
|
||||
|
||||
<h4 class="mb-4">Commentaires</h4>
|
||||
|
||||
@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))
|
||||
{
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="d-flex align-items-start my-3">
|
||||
|
||||
<!-- Image -->
|
||||
<a asp-action="Details"
|
||||
<a asp-action="Index"
|
||||
asp-route-id="@titre.IdTitre"
|
||||
class="me-3 text-black">
|
||||
<img src="@titre.UrlJaquette" alt="@titre.Libelle" width="70px" height="70px" class="object-fit-cover" loading="lazy"/>
|
||||
@@ -41,7 +41,7 @@
|
||||
@titre.ArtisteNom
|
||||
</a>
|
||||
-
|
||||
<a asp-action="Details"
|
||||
<a asp-action="Index"
|
||||
asp-route-id="@titre.IdTitre">
|
||||
@titre.Libelle
|
||||
</a>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
layout="${longdate}|${level:uppercase=true}|${logger}|${message} ${exception:format=tostring}|${aspnet-request-url:whenEmpty=NoRequest}" />
|
||||
|
||||
<!-- Console pour debug immédiat -->
|
||||
<target xsi:type="Console" name="Info"
|
||||
<target xsi:type="Console" name="console"
|
||||
layout="${longdate}|${level:uppercase=true}|${logger}|${message}" />
|
||||
</targets>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user