14-2 Panier de base lié à la session
À la différence de votre projet, le projet d'exemple Snowfall ne permet pas de s'inscrire à plusieurs événements à la fois. Cependant, faisons comme si nous avions un panier, quoique l'implémentation sera plus simple que ce que vous avez à faire dans votre projet.
En effet, lorsque l'utilisateur signifie son intérêt de s'inscrire à un événement, on le sauvegarde afin de lui permettre d'y revenir plus tard et de compléter son achat.
Pour conserver cet état, on peut utiliser le concept de Session
, tel que vu d'entrée de jeu dans l'introduction. La session est similaire au localstorage dans le sens qu'elle permet de stocker des données localement dans le navigateur via un cookie, mais elle a aussi l'avantage d'être transférée automatiquement au serveur lors de chaque requête et automatiquement rendu accessible par le serveur/framework.
En effet, la session est stockée dans un cookie et ces derniers sont transmis au serveur lors de chaque requête. Le serveur décrypte le cookie quand il le reçoit, le lit et rend disponibles les informations (clé/valeurs) contenues dans ce dernier.
C'est un moyen très simple et très polyvalent de conserver un état entre le client et le serveur.
Le séquence est grosso modo la suivante:
- J'ajoute un item au "panier"
- Le serveur crée un cookie de session et assigne l'item à la clé "Panier" de la session
- Le cookie est transféré à chaque requête, ce qui permet au serveur d'avoir constamment l'état du panier du client, ou à tout de moins de sa session d'achat.
Définir le processus d'ajout au panier
Le processus d'ajout au panier ressemble à ceci:
- À partir d'une page d'événement, je clique sur le bouton
Acheter un billet
- Le bouton fait une requête
POST
versPanierController#Create
(/panier
), en incluant leid
de l'événement dans le détail de la requêtePOST
- L'action
Acheter
du contrôleur ajoute à la clépanier
de la session leid
de l'événementé - L'utilisateur est redirigé vers une vue montrant le contenu de son "panier"
Créer un contrôleur PanierController
- Sous le dossier
Controllers
du projetMvc
, ajoutez un nouveau contrôleur - Nommez ce contrôleur
PanierController
. N'oubliez pas de définir sa route de base de contrôleur.Snowfall.Web.Mvc/Controllers/PanierController.cs[Route("[controller]")]
public class PanierController : Controller
{
// GET
public IActionResult Index()
{
return View();
}
} - Supprimez l'action
Index
générée automatiquement et créez une actionCreate
, pour une requêtePOST
qui permettra d'ajouter un item au panier.Snowfall.Web.Mvc/Controllers/PanierController.cs[Route("[controller]")]
public class PanierController : Controller
{
public async Task<IActionResult> Create()
{
}
} - Faites-en sorte que l'action
Create
réponde aux requêtesPOST
[HttpPost]
public async Task<IActionResult> Create()
{
} - Retournez simplement
Ok()
pour satisfaire le compilateur pour le moment.[HttpPost]
public async Task<IActionResult> Create()
{
return Ok();
}
Ajouter l'événement à la session d'achat (panier)
Lorsque l'utilisateur témoigne son intention de s'inscrire à un événement, on ajoutera l'événement à la session dans une clé Panier
. Cette dernière ne contiendra qu'un élément, mais le concept pourrait facilement être étendu à plusieurs éléments
Lier un bouton d'achat à PanierController#Create
Pour ajouter un billet au panier et procéder à l'achat, il nous faut créer un bouton d'achat dans la vue d'un événement et lier ce dernier à l'action Create
responsable de mettre l'item dans la session.
Dans la vue Show
des événements, on peut ajouter un bouton quelque part dans la page pour procéder à l'achat. Par exemple, sous le libellé du nom de la ville.
<!-- ... -->
<div class="col-md-6">
<p>@Model.Description</p>
<p><strong>@Model.Prix$</strong></p>
<ul class="list-unstyled small">
<li>Date: @Model.Date.ToShortDateString()</li>
<li>Capacité: @Model.Capacite places</li>
</ul>
<span class="badge text-bg-light">@Model.Ville!.Nom, @Model.Ville!.PaysIso</span>
<form asp-controller="Panier" asp-action="Create">
<button class="btn btn-primary mt-5">Acheter un billet</button>
</form>
</div>
<!-- ... -->
- Ce bouton, camouflé dans un formulaire, fera une requête
POST
versasp-controller="Panier" asp-action="Create"
Vous pouvez essayer. Un bouton devrait être présent dans la page, mais vous serez mené vers une page vide puisque l'action Create
ne retourne qu'un code 200. De plus, on ne passe aucune donnée au contrôleur: on ne sait pas quel événement ajouter au panier.
Ajouter un champ caché pour contenir l'identifiant de l'événement
La requête POST
doit envoyer l'événement à ajouter au panier.
Pour cela, on peut utiliser un champ caché (hidden
) dans la page:
<form asp-controller="Panier" asp-action="Create">
<input type="hidden" name="itemId" value="@Model.Id" />
<button class="btn btn-primary mt-5">Acheter un billet</button>
</form>
Dans le projet d'exemple, les quantités ne sont pas gérées. Dans votre projet, n'oubliez pas que vous devez permettre à l'utilisateur de choisir la quantité qu'il veut ajouter à son panier!
Lier le formulaire à un ViewModel
Comme vu jusqu'à maintenant, nous pourrions faire cela au niveau de notre contrôleur pour accepter le champ itemId
du formulaire en argument:
public async Task<IActionResult> Create(int itemId)
{
return Ok();
}
Et jusque dans une certaine mesure, si vous partez l'application en mode debug
, mettez un breakpoint
dans la fonction Create
, vous verrez que cela fonctionne:
Par contre, conceptuellement, j'ajoute au panier des items. Ces items ont minimalement un Id
, mais pourraient avoir d'autres propriétés comme une quantité, par exemple.
Ainsi, plutôt que d'accepter des paramètres individuellement, il est possible d'associer à un formulaire un ViewModel
.
- Créer dans le dossier
Models
du projetMvc
une classePanierItemViewModel
sous un dossierPanier
:Snowfall.Web.Mvc/Models/Panier/PanierItemViewModel.csnamespace Snowfall.Web.Mvc.Models.Panier;
public class PanierItemViewModel
{
} - Ajoutez une propriété
ItemId
pour contenir l'identifiant de l'item du panier.Snowfall.Web.Mvc/Models/Panier/PanierItemViewModel.cspublic class PanierItemViewModel
{
public int ItemId { get; set; }
} - Modifiez la signature de la fonction
Create
pour accepter leViewModel
PanierItemViewModel
.Snowfall.Web.Mvc/Controllers/PanierController.cspublic async Task<IActionResult> Create(PanierItemViewModel panierItem)
{
return Ok();
}
Les input
du formulaire possédant le même nom (name
) que les propriétés du ViewModel
seront automatiquement associées!
Ajouter l'item à la session
Pour ajouter des données à la session, on utilise un système clé-valeur
via l'objet HttpContext.Session
.
Pour assigner des valeurs, la session possède deux fonctions:
HttpContext.Session.SetInt32();
: stock unint
dans la sessionHttpContext.Session.SetString();
: stock unstring
dans la session
La session est stockée dans un cookie qui est un format de stockage texte
. Ainsi, il n'est pas possible de stocker directement un objet, par exemple.
Pour stocker un objet, il faut le transformer en texte. C'est un processus que nous appelons sérialisation
.
Heureusement, nous connaissons un format de données qui est particulièrement bien adapté aux objets: le JSON!
.NET a une classe statique appelée JsonSerializer
permettant de convertir au format JSON un objet.
Convertir le ViewModel en JSON
On peut convertir l'objet en JSON de cette façon:
public async Task<IActionResult> Create(PanierItemViewModel panierItem)
{
string itemSerialized = JsonSerializer.Serialize(panierItem);
return Ok();
}
Utiliser SetString
sur la session
Finalement, on peut ajouter le tout à la session à l'aide de HttpContext.Session.SetString
.
public async Task<IActionResult> Create(PanierItemViewModel panierItem)
{
string itemSerialized = JsonSerializer.Serialize(panierItem);
HttpContext.Session.SetString("panier", itemSerialized);
return Ok();
}
La session est un système "clé-valeur", ce qui veut dire qu'on associe une valeur à une certaine clé. Ici, la clé choisie est panier
.
C'est la même clé que nous utiliserons pour lire le contenu de la session.
Faites un ajout au panier à l'aide du bouton d'achat et vous pourrez voir un cookie Session
dans les outils de développeur de votre navigateur! :)