(Rebase) Resolution de merge conflit entre dev et j3/feat/pagination
This commit is contained in:
@@ -26,12 +26,7 @@
|
||||
<ProjectReference Include="..\Webzine.Business.Contracts\Webzine.Business.Contracts.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Entity\Webzine.Entity.csproj" />
|
||||
<ProjectReference Include="..\Webzine.Repository.Contracts\Webzine.Repository.Contracts.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions">
|
||||
<HintPath>..\..\..\..\..\..\..\.nuget\packages\microsoft.extensions.logging.abstractions\10.0.5\lib\net10.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0-preview.1.25080.5" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -94,7 +94,6 @@ public class DbStyleRepository : IStyleRepository
|
||||
this.logger.LogDebug("Recherche du style avec l'ID: {Id}", id);
|
||||
|
||||
var style = this.context.Styles
|
||||
.AsNoTracking()
|
||||
.Include(s => s.Titres)
|
||||
.SingleOrDefault(s => s.IdStyle == id);
|
||||
|
||||
@@ -123,7 +122,6 @@ public class DbStyleRepository : IStyleRepository
|
||||
this.logger.LogDebug("Tri des styles par libellé");
|
||||
|
||||
var styles = this.context.Styles
|
||||
.AsNoTracking()
|
||||
.OrderBy(s => s.Libelle);
|
||||
|
||||
this.logger.LogDebug("La liste de styles a été récupérée.");
|
||||
|
||||
@@ -74,14 +74,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
|
||||
{
|
||||
@@ -121,18 +113,16 @@ public class ArtisteController : Controller
|
||||
[HttpPost]
|
||||
public IActionResult Edit(ArtisteEditViewModel model)
|
||||
{
|
||||
var artiste = new Artiste
|
||||
{
|
||||
IdArtiste = model.Id,
|
||||
Nom = model.Nom,
|
||||
Biographie = model.Biographie,
|
||||
};
|
||||
var artiste = this.artisteRepository.Find(model.Id);
|
||||
|
||||
if (!this.ModelState.IsValid)
|
||||
if (artiste == null)
|
||||
{
|
||||
return this.View(artiste);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
artiste.Nom = model.Nom;
|
||||
artiste.Biographie = model.Biographie;
|
||||
|
||||
this.artisteRepository.Update(artiste);
|
||||
|
||||
return this.RedirectToAction("Index");
|
||||
|
||||
@@ -96,11 +96,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);
|
||||
|
||||
@@ -5,8 +5,11 @@ using Microsoft.AspNetCore.Mvc;
|
||||
using Webzine.Business.Contracts;
|
||||
using Webzine.Business.Contracts.Dto;
|
||||
|
||||
/// <summary>
|
||||
/// Contrôleur pour gérer le tableau de bord de l'administration.
|
||||
/// </summary>
|
||||
[Area("Administration")]
|
||||
public class DashboardController : Controller // TODO à refaire
|
||||
public class DashboardController : Controller
|
||||
{
|
||||
private readonly ILogger<DashboardController> logger;
|
||||
private readonly IDashboardService dashboardService;
|
||||
|
||||
@@ -2,9 +2,10 @@ namespace Webzine.WebApplication.Areas.Administration.Controllers;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
using ViewModels.Styles;
|
||||
|
||||
using Webzine.Entity;
|
||||
using Webzine.Repository.Contracts;
|
||||
using Webzine.WebApplication.Areas.Administration.ViewModels.Style;
|
||||
|
||||
/// <summary>
|
||||
/// Controleur pour la gestion des styles dans l'administration du webzine.
|
||||
@@ -77,11 +78,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,
|
||||
@@ -138,7 +134,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);
|
||||
@@ -165,11 +160,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)
|
||||
{
|
||||
|
||||
@@ -114,13 +114,35 @@ public class TitreController : Controller
|
||||
[HttpPost]
|
||||
public IActionResult Create(TitreAdminDTO model)
|
||||
{
|
||||
if (this.ModelState.IsValid)
|
||||
if (!this.ModelState.IsValid)
|
||||
{
|
||||
this.titreAdminService.CreerTitre(model);
|
||||
return this.RedirectToAction("Index");
|
||||
var form = new AdminTitreForm
|
||||
{
|
||||
IdArtiste = model.IdArtiste,
|
||||
Libelle = model.Libelle,
|
||||
Album = model.Album,
|
||||
Chronique = model.Chronique,
|
||||
DateSortie = model.DateSortie,
|
||||
Duree = model.Duree,
|
||||
UrlJaquette = model.UrlJaquette,
|
||||
UrlEcoute = model.UrlEcoute,
|
||||
Styles = model.Styles,
|
||||
Artistes = this.artisteRepository.FindAll().Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom,
|
||||
}).ToList(),
|
||||
AllStyles = this.styleRepository.FindAll().Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle,
|
||||
}).ToList(),
|
||||
};
|
||||
return this.View(form);
|
||||
}
|
||||
|
||||
return this.View(model);
|
||||
this.titreAdminService.CreerTitre(model);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,13 +193,36 @@ public class TitreController : Controller
|
||||
[HttpPost]
|
||||
public IActionResult Edit(TitreAdminDTO model)
|
||||
{
|
||||
if (this.ModelState.IsValid)
|
||||
if (!this.ModelState.IsValid)
|
||||
{
|
||||
this.titreAdminService.ModifierTitre(model);
|
||||
return this.RedirectToAction("Index");
|
||||
var form = new AdminTitreForm
|
||||
{
|
||||
Id = model.Id,
|
||||
IdArtiste = model.IdArtiste,
|
||||
Libelle = model.Libelle,
|
||||
Album = model.Album,
|
||||
Chronique = model.Chronique,
|
||||
DateSortie = model.DateSortie,
|
||||
Duree = model.Duree,
|
||||
UrlJaquette = model.UrlJaquette,
|
||||
UrlEcoute = model.UrlEcoute,
|
||||
Styles = model.Styles,
|
||||
Artistes = this.artisteRepository.FindAll().Select(a => new SelectListItem
|
||||
{
|
||||
Value = a.IdArtiste.ToString(),
|
||||
Text = a.Nom,
|
||||
}).ToList(),
|
||||
AllStyles = this.styleRepository.FindAll().Select(s => new SelectListItem
|
||||
{
|
||||
Value = s.IdStyle.ToString(),
|
||||
Text = s.Libelle,
|
||||
}).ToList(),
|
||||
};
|
||||
return this.View(form);
|
||||
}
|
||||
|
||||
return this.View(model);
|
||||
this.titreAdminService.ModifierTitre(model);
|
||||
return this.RedirectToAction("Index");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -14,11 +14,11 @@
|
||||
/// <summary>
|
||||
/// Définit le nom de l'artiste.
|
||||
/// </summary>
|
||||
public string Nom { get; set; }
|
||||
public string? Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la biographie de l'artiste.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; }
|
||||
public string? Biographie { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -10,12 +10,15 @@
|
||||
/// <summary>
|
||||
/// Nom de l'artiste.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Required(ErrorMessage = "Le nom de l'auteur est obligatoire.")]
|
||||
[StringLength(50, ErrorMessage = "Le nom ne doit pas dépasser 50 caractères.")]
|
||||
public string Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Biographie de l'artiste.
|
||||
/// </summary>
|
||||
/// </summary>*
|
||||
[Required(ErrorMessage = "La biographie ne peux pas etre vide.")]
|
||||
|
||||
public string Biographie { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,11 @@
|
||||
/// <summary>
|
||||
/// Nom de l'artiste.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Required(ErrorMessage = "Le nom de l'auteur est obligatoire.")]
|
||||
[StringLength(50, ErrorMessage = "Le nom ne doit pas dépasser 50 caractères.")]
|
||||
|
||||
public string Nom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Biographie de l'artiste.
|
||||
/// </summary>
|
||||
public string Biographie { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Copyright (c) Equipe 1 - BOBIN, MASI, NODON, VETU. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Styles
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Required(ErrorMessage = "Le libelle du style est obligatoire.")]
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Copyright (c) Equipe 1 - BOBIN, MASI, NODON, VETU. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Styles
|
||||
{
|
||||
/// <summary>
|
||||
/// ViewModel pour la suppression d'un style en administration.
|
||||
@@ -17,6 +17,6 @@ namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
/// <summary>
|
||||
/// Obtient ou définit le libellé du style.
|
||||
/// </summary>
|
||||
public string Libelle { get; set; }
|
||||
public string? Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Copyright (c) Equipe 1 - BOBIN, MASI, NODON, VETU. All rights reserved.
|
||||
// </copyright>
|
||||
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Styles
|
||||
{
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Webzine.WebApplication.Areas.Administration.ViewModels.Style
|
||||
/// <summary>
|
||||
/// Obtient ou definit le libelle du style.
|
||||
/// </summary>
|
||||
[Required]
|
||||
[Required(ErrorMessage = "Le libelle du style est obligatoire.")]
|
||||
public string Libelle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
namespace Webzine.WebApplication.Areas.Administration.ViewModels.Titre;
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
using Microsoft.AspNetCore.Mvc.Rendering;
|
||||
|
||||
/// <summary>
|
||||
@@ -15,36 +17,47 @@ public class AdminTitreForm
|
||||
/// <summary>
|
||||
/// Définit l'identifiant de l'artiste associé au titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "L'id de l'artiste est obligatoire.")]
|
||||
|
||||
public int IdArtiste { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le titre du titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "Le labelle est obligatoire.")]
|
||||
|
||||
public string Libelle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit le nom de l'album associé au titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "L'album est obligatoire.")]
|
||||
|
||||
public string Album { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la chronique du titre, peut-être une critique ou une description du titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "La chronique est obligatoire.")]
|
||||
public string Chronique { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la date de sortie du titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "La date de est obligatoire.")]
|
||||
|
||||
public DateTime DateSortie { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit la durée du titre en secondes.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "La durée est obligatoire.")]
|
||||
public int Duree { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Définit l'URL de la jaquette de l'album associé au titre.
|
||||
/// </summary>
|
||||
[Required(ErrorMessage = "L'Url de la jaquette est obligatoire.")]
|
||||
public string UrlJaquette { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
<label class="col-md-3 col-form-label">Nom de l'artiste<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Nom" class="form-control" />
|
||||
<span asp-validation-for="Nom" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -19,6 +21,8 @@
|
||||
<label class="col-md-3 col-form-label">Biographie</label>
|
||||
<div class="col-md-9">
|
||||
<textarea asp-for="Biographie" class="form-control" rows="5"></textarea>
|
||||
<span asp-validation-for="Biographie" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<label class="col-md-3 col-form-label">Nom de l'artiste<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Nom" class="form-control" />
|
||||
<span asp-validation-for="Nom" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -14,7 +16,10 @@
|
||||
<label class="col-md-3 col-form-label">Biographie</label>
|
||||
<div class="col-md-9">
|
||||
<textarea asp-for="Biographie" class="form-control" rows="5"></textarea>
|
||||
<span asp-validation-for="Biographie" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- BOUTONS -->
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleCreateViewModel
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Styles.StyleCreateViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Créer un style";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleDeleteViewModel
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Styles.StyleDeleteViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Supprimer un style";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Style.StyleEditViewModel
|
||||
@model Webzine.WebApplication.Areas.Administration.ViewModels.Styles.StyleEditViewModel
|
||||
|
||||
@{
|
||||
ViewData["Title"] = "Editer un style";
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
<select asp-for="IdArtiste"
|
||||
asp-items="Model.Artistes"
|
||||
class="form-select"></select>
|
||||
<span asp-validation-for="IdArtiste" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +18,7 @@
|
||||
<label class="col-md-3 col-form-label">Titre<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Libelle" class="form-control"/>
|
||||
<span asp-validation-for="Libelle" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -24,6 +27,7 @@
|
||||
<label class="col-md-3 col-form-label">Album<span class="text-danger">*</span></label>
|
||||
<div class="col-md-9">
|
||||
<input asp-for="Album" class="form-control"/>
|
||||
<span asp-validation-for="Album" class="text-danger"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -34,6 +38,8 @@
|
||||
<textarea asp-for="Chronique"
|
||||
class="form-control"
|
||||
rows="5"></textarea>
|
||||
<span asp-validation-for="Chronique" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -55,6 +61,8 @@
|
||||
class="form-control"
|
||||
type="number"
|
||||
min="0" />
|
||||
<span asp-validation-for="Duree" class="text-danger"></span>
|
||||
|
||||
<span class="input-group-text text-muted">seconds</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -66,6 +74,8 @@
|
||||
<div class="col-md-9">
|
||||
<input asp-for="UrlJaquette"
|
||||
class="form-control"/>
|
||||
<span asp-validation-for="UrlJaquette" class="text-danger"></span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
///
|
||||
/// </summary>
|
||||
public class ArtisteController : Controller
|
||||
{
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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)
|
||||
|
||||
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,7 +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<GlobalExceptionFilter>();
|
||||
options.Filters.Add<ValidationActionFilter>();
|
||||
})
|
||||
|
||||
// Ajoute la compilation des vues lors de l'execution de l'application.
|
||||
// Cela nous evite de recompiler l'application a chaque modification de vue.
|
||||
@@ -87,6 +92,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();
|
||||
|
||||
@@ -60,13 +60,14 @@
|
||||
<div class="d-flex gap-2">
|
||||
|
||||
<form asp-action="Like" method="post">
|
||||
<input type="hidden" name="IdTitre" value="@Model.Details.IdTitre" />
|
||||
<input type="hidden" name="IdTitre" value="@Model.Details.IdTitre"/>
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm">
|
||||
<i class="fa fa-thumbs-up me-1"></i> Like
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<a asp-area="Administration" asp-controller="Titre" asp-action="Edit" asp-route-id="@Model.Details.IdTitre" class="btn text-primary btn-sm">
|
||||
<a asp-area="Administration" asp-controller="Titre" asp-action="Edit"
|
||||
asp-route-id="@Model.Details.IdTitre" class="btn text-primary btn-sm">
|
||||
<i class="fa fa-pen-to-square me-1"></i> Editer
|
||||
</a>
|
||||
|
||||
@@ -88,7 +89,7 @@
|
||||
class="img-fluid rounded shadow"
|
||||
alt="Jaquette"
|
||||
loading="lazy"
|
||||
fetchpriority="high" />
|
||||
fetchpriority="high"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -121,7 +122,7 @@
|
||||
<input name="Auteur"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre nom"
|
||||
required />
|
||||
required/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -131,10 +132,10 @@
|
||||
</label>
|
||||
<div class="col">
|
||||
<textarea name="Contenu"
|
||||
rows="3"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre commentaire..."
|
||||
required></textarea>
|
||||
rows="3"
|
||||
class="form-control input-full"
|
||||
placeholder="Votre commentaire..."
|
||||
required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -156,7 +157,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))
|
||||
{
|
||||
@@ -169,7 +170,7 @@
|
||||
width="50"
|
||||
height="50"
|
||||
class="rounded-circle me-3 shadow-sm"
|
||||
alt="avatar" />
|
||||
alt="avatar"/>
|
||||
|
||||
<div>
|
||||
<strong>@comment.Auteur</strong>,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"EfPerformance": {
|
||||
"SeuilMs": 10
|
||||
"SeuilMs": 60
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user