28-3 Modification (Update)
Prochain arrêt, être en mesure de modifier des événements via l'API!
Coquille Contrôleur/Service/Repository
Pour procéder à une modification d'événement, le contrôleur d'API recevra la requête HTTP à traiter, appellera ensuite le service EvenementService
qui pour sa part appellera la couche de données via EvenementRepository
pour procéder à la sauvegarde.
DTO - ModifierEvenementDto
Pour la modification, techniquement la modification pourrait être partielle. Nous pourrions modifier seulement le nom, par exemple. Ainsi, les propriétés doivent être optionnelles.
- Sous le dossier
Snowfall.Application/Dtos/Evenements
, créez un fichierModifierEvenementDto
- Mettez les propriétés optionnelles
Snowfall.Application/Dtos/Evenements/ModifierEvenementDto.cs
public class ModifierEvenementDto
{
public string? Nom { get; set; } = null!;
public string? Description { get; set; }
public string? ImagePath { get; set; }
public DateTime? Date { get; set; }
public Decimal? Prix { get; set; }
public int? Capacite { get; set; }
public int? VilleId { get; set; }
}
Contrôleur - EvenementsController#Update
Pour créer un événement via l'API, il sera nécessaire d'avoir une action d'API répondant à la méthode HTTP PATCH
et recevant en paramètre EvenementDto
, en plus du id
de l'événement à modifier. Ce dernier provient de la route.
/// <summary>
/// PATCH /api/evenements/{id}
/// Permets de modifier un événement
/// </summary>
/// <param name="id">Le id de l'événement à modifier</param>
/// <param name="modifierEvenementDto">Le DTO de l'événement modifié à sauvegarder</param>
/// <returns></returns>
[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
throw new NotImplementedException();
}
Repository - EvenementRepository#Update
Dans un second temps, ajoutons à l'interface du repository la fonction Update
qui prend en paramètre un événement et retourne si la modification a été un succès.
public interface IEvenementRepository
{
Task<List<Evenement>> GetAll();
Task<List<Evenement>> FindByVilleId(int villeId);
Task<Evenement?> FindById(int id);
Task<Evenement> Create(Evenement evenement);
Task<bool> Update(Evenement evenement);
}
Ensuite, il est possible de faire Implement missing members
à partir de l'aide contextuelle de Rider dans la classe EvenementRepository
. Cela ajoutera la fonction Update
à l'implémentation du repository.
public async Task<bool> Update(Evenement evenement)
{
throw new NotImplementedException();
}
Vous devez implémenter vous-même le détail de la fonction de modification du repository, soit la modification dans la base de données. Vous pouvez vous baser sur la fonction de modification de question.
Service - EvenementService#Create
Finalement, dans l'interface IEvenementService
, on ajoute la fonction Update
:
public interface IEvenementService
{
Task<List<Evenement>> GetAll();
Task<List<Evenement>> FindByVilleId(int? villeId);
Task<Evenement?> FindById(int id);
Task<Evenement> Create(Evenement evenement);
Task<bool> Update(Evenement evenement);
}
Pour ensuite implémenter via Implement missing members
la fonction dans EvenementService
et appeler le repository.
public async Task<bool> Update(Evenement evenement)
{
return await _evenementRepository.Update(evenement);
}
Compléter EvenementController#Update
Si vous avez complété la requête SQL au niveau du repository pour la mise à jour, nous avons tous les éléments en place pour compléter la fonction Update
du contrôleur d'événements.
-
Premièrement, on récupère l'év énement pour vérifier qu'il existe bien.
Snowfall.Web.Api/Controllers/EvenementsController.cs[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
var evenement = await _evenementService.FindById(id);
if (evenement == null)
return NotFound();
} -
Ensuite, on
map
le DTO reçu enEvenement
pour être en mesure d'envoyer le tout à la coucheService
. Par contre,AutoMapper
ne sera pas d'une grande aide ici :(attentionAutoMapper est peu adapté aux mises à jour partielles avec des champs optionnels. Recevoir une mise à jour qui ne mettrais à jour que le
nom
de l'événement aurait pour conséquence de remplacer le reste des champs par des valeursnull
ou encore0
dans le cas d'un int.Bref, on fera l'association manuellement 😬
Snowfall.Web.Api/Controllers/EvenementsController.cs[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
var evenement = await _evenementService.FindById(id);
if (evenement == null)
return NotFound();
if (modifierEvenementDto.VilleId != null) evenement.VilleId = modifierEvenementDto.VilleId.Value;
if (modifierEvenementDto.Description != null) evenement.Description = modifierEvenementDto.Description;
if (modifierEvenementDto.Nom != null) evenement.Nom = modifierEvenementDto.Nom;
if (modifierEvenementDto.ImagePath != null) evenement.ImagePath = modifierEvenementDto.ImagePath;
if (modifierEvenementDto.Date != null) evenement.Date = modifierEvenementDto.Date.Value;
if (modifierEvenementDto.Capacite != null) evenement.Capacite = modifierEvenementDto.Capacite.Value;
if (modifierEvenementDto.Prix != null) evenement.Prix = modifierEvenementDto.Prix.Value;
}Tout ça ajoute beaucoup de lignes au contrôleur cependant. C'est pourquoi je vous suggère d'ajouter une méthode d'extension sur le type
ModifierEvenementDto
afin de faire le mappage dans une fonction dédiée. -
Créez un dossier
Mappings
sous le projetApplication
-
Créez une classe
EvenementMappingExtensions
sous ce nouveau dossierMappings
-
Faites de la classe une classe
static
et ajoutez une fonctionApplyTo
Snowfall.Application/Mappings/EvenementMappingExtensions.cspublic static class EvenementMappingExtensions
{
public static void ApplyTo(this ModifierEvenementDto src, Evenement dest)
{
}
}infoOn se rappelle que les méthodes d'extension doivent être dans une clase static.
Ensuite, on remarque que la fonction
ApplyTo
sera ajoutée au typeModifierEvenementDto
. La fonction fera la conversion entreModifierEvenementDto
(src
) etEvenement
(dest
). -
Copiez votre mapping dans cette fonction.
Snowfall.Application/Mappings/EvenementMappingExtensions.cspublic static class EvenementMappingExtensions
{
public static void ApplyTo(this ModifierEvenementDto src, Evenement dest)
{
if (src.VilleId != null) dest.VilleId = src.VilleId.Value;
if (src.Description != null) dest.Description = src.Description;
if (src.Nom != null) dest.Nom = src.Nom;
if (src.ImagePath != null) dest.ImagePath = src.ImagePath;
if (src.Date != null) dest.Date = src.Date.Value;
if (src.Capacite != null) dest.Capacite = src.Capacite.Value;
if (src.Prix != null) dest.Prix = src.Prix.Value;
}
}infoAssurez-vous de remplacer
modifierEvenementDto
parsrc
etevenement
pardest
.Vous pouvez utiliser la fonction
Refactor
->Rename
de Rider. -
Appelez
ApplyTo
dans le contrôleurSnowfall.Web.Api/Controllers/EvenementsController.cs[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
var evenement = await _evenementService.FindById(id);
if (evenement == null)
return NotFound();
modifierEvenementDto.ApplyTo(evenement);
}Beaucoup plus propre! :)
-
On procède finalement à la mise à jour via le service. Dans le cas où le résultat serait
false
, et donc que la mise à jour n'a pas été un succès, on retournera le code d'erreur422 Unprocessable Content
. Ce code d'erreur stipule que le serveur a reçu la requête, qu'elle est valide, mais pour une raison quelconque n'a pas pu traiter la requête correctement.Snowfall.Web.Api/Controllers/EvenementsController.cs[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
var evenement = await _evenementService.FindById(id);
if (evenement == null)
return NotFound();
modifierEvenementDto.ApplyTo(evenement);
bool updated = await _evenementService.Update(evenement);
if (!updated)
return UnprocessableEntity();
} -
Ensuite, on map à nouveau l'événement en un
EvenementDto
dans le but de le retourner.Snowfall.Web.Api/Controllers/EvenementsController.cs[HttpPatch("{id:int}")]
public async Task<IActionResult> Update(int id, ModifierEvenementDto modifierEvenementDto)
{
var evenement = await _evenementService.FindById(id);
if (evenement == null)
return NotFound();
modifierEvenementDto.ApplyTo(evenement);
bool updated = await _evenementService.Update(evenement);
if (!updated)
return UnprocessableEntity();
return Ok(_mapper.Map<EvenementDto>(evenement));
}
Tester via Postman
ou Scalar
Assurez-vous d'avoir implémenté la fonction de modification Update
du repository!
Pour tester, vous pouvez utiliser Postman
.
- Assurez-vous que votre projet .NET est démarré et recompilé pour contenir les derniers changements
- Assurez-vous de choisir
PATCH
comme méthodeHTTP
- Entrez comme URL http://[url-de-votre-projet]/api/evenements/[id-evenement]
- Sélectionner
Body/raw/JSON
et entrer leJSON
modifié pour modifier l'événement souhaité.
Pour obtenir un JSON d'événement à modifier, faites premièrement une requête GET
vers l'événement à modifier.
Vous recevrez la version actuelle de cet événement que vous pourrez ensuite utiliser comme base pour la modification!
Procédez ensuite à la requête de modification: