Skip to content

Commit ea36382

Browse files
Identité côté client
1 parent 02a51c6 commit ea36382

File tree

2 files changed

+158
-4
lines changed

2 files changed

+158
-4
lines changed

web/docs/01-notes/18-rencontre9.2.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ public class Comment{
7474
public string Text { get; set; } = null!;
7575

7676
[InverseProperty("Comments")]
77-
public User Author { get; set; } = null!;
77+
public virtual User Author { get; set; } = null!;
7878

7979
[InverseProperty("Upvotes")]
80-
public List<User> Upvoters { get; set; } = new List<User>();
80+
public virtual List<User> Upvoters { get; set; } = new List<User>();
8181

8282
[InverseProperty("Downvotes")]
83-
public List<User> Downvoters { get; set; } = new List<User>();
83+
public virtual List<User> Downvoters { get; set; } = new List<User>();
8484
}
8585
```
8686

web/docs/01-notes/22-rencontre11.2.md

Lines changed: 155 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,158 @@ public async Task<IActionResult> PostRole(string roleName){
191191
else return BadRequest(new { Message = "La création du rôle a échoué." });
192192

193193
}
194-
```
194+
```
195+
196+
## 👤 Identité côté client
197+
198+
Parfois, côté client, on souhaite :
199+
200+
* Cacher certains boutons ou menus qui sont seulement disponibles pour certains **rôles**.
201+
* Cacher certains boutons ou menus qui sont seulement disponibles pour les utilisateurs **authentifiés**.
202+
* Afficher le **nom d'utilisateur** de ... l'utilisateur, s'il est connecté.
203+
* etc.
204+
205+
Problème : La **gestion des utilisateurs** existe seulement **côté serveur**. Il n'y a pas de notions de `User` ou de `Role` **côté client**.
206+
207+
Il est tout de même possible de *bricoler* des solutions pour réaliser les défis mentionnés ci-dessus, mais il faut garder à l'esprit que cela ne permettra jamais de **sécuriser** l'application, seulement de **raffiner** l'apparence. Pour rappel, les utilisateurs ont **accès à tout le code** des composants qui sont `"use client";` !
208+
209+
⛔ Gardons tout de même à l'esprit que les utilisateurs n'aiment pas voir des menus ou boutons qui ne leur sont pas destinés.
210+
211+
### 🔑 Données de connexion
212+
213+
Pour rappel, lorsqu'on se **connecte**, on envoyait le token à l'application cliente :
214+
215+
```cs showLineNumbers
216+
return Ok(new
217+
{
218+
token = new JwtSecurityTokenHandler().WriteToken(token), // Token !
219+
validTo = token.ValidTo
220+
});
221+
```
222+
223+
Or, on peut également envoyer d'**autres informations** si on veut !
224+
225+
```cs showLineNumbers
226+
return Ok(new
227+
{
228+
token = new JwtSecurityTokenHandler().WriteToken(token),
229+
validTo = token.ValidTo,
230+
username = user.UserName, // Pseudo !
231+
roles = roles // List<string> des rôles !
232+
});
233+
```
234+
235+
Côté client, on peut récupérer ces informations et les utiliser pour cacher des menus et boutons ou personnaliser l'apparence de l'interface selon l'identité.
236+
237+
```tsx showLineNumbers
238+
async function login(loginDTO : any){
239+
240+
const x = await axios.post(domain + "api/Users/Login", loginDTO);
241+
console.log(x.data);
242+
243+
// 🔑 On stocke le token... et les autres infos !
244+
sessionStorage.setItem("token", x.data.token);
245+
sessionStorage.setItem("username", x.data.username);
246+
sessionStorage.setItem("roles", JSON.stringify(x.data.roles));
247+
248+
// 📬 Ça peut aussi être dans des états
249+
setUsername(x.data.username);
250+
setRoles(x.data.roles);
251+
252+
// 🤷‍♂️ On peut aussi retourner les données pour qu'une autre fonction les utilise
253+
return x.data;
254+
255+
}
256+
```
257+
258+
:::tip
259+
260+
✅ Stocker les données de l'utilisateur dans des **états** sera très intéressant pour gérer des **affichages conditionnels** dans le HTML. Cela dit, les données seront perdues si on réactualise la page.
261+
262+
💾 Stocker les données de l'utilisateur dans le **stockage du navigateur** n'est pas très pratique pour gérer les affichages conditionnels, mais c'est parfait pour s'assurer que les données puissent être récupérées avec `useEffect()` et les faire perdurer malgré un *reload*.
263+
264+
Combinez les **deux** stratégies autant que possible. Les **Contexts** pourraient même servir afin de **partager** ces données entre **plusieurs commposants** dans certains cas.
265+
266+
:::
267+
268+
### 📦 DTOs différenciés selon l'identité
269+
270+
Une autre stratégie possible est d'exploiter les *DisplayDTOs*. Par exemple, voici, les classes `Comment.cs` et `CommentDisplayDTO.cs`. Bien entendu, ce sont des `CommentDisplayDTO` qui seront envoyés au **client** car ils sont **plus adaptés** au projet **Next.js**.
271+
272+
<Tabs>
273+
<TabItem value="cs1" label="Comment.cs">
274+
```cs showLineNumbers
275+
public class Comment{
276+
277+
public int Id { get; set; }
278+
public string Text { get; set; } = null!;
279+
280+
[InverseProperty("Comments")]
281+
public virtual User Author { get; set; } = null!;
282+
283+
[InverseProperty("Upvotes")]
284+
public virtual List<User> Upvoters { get; set; } = new List<User>();
285+
286+
[InverseProperty("Downvotes")]
287+
public virtual List<User> Downvoters { get; set; } = new List<User>();
288+
}
289+
```
290+
</TabItem>
291+
<TabItem value="cs2" label="CommentDisplayDTO.cs" default>
292+
```cs showLineNumbers
293+
public class CommentDisplayDTO{
294+
295+
public int Id { get; set; }
296+
public string Text { get; set; } = null!;
297+
public string Author { get; set; } = null!; // Simple pseudo plutôt qu'objet User
298+
public int Upvotes { get; set; } // Nombre d'upvotes plutôt que la liste des upvoters
299+
public int Downvotes { get; set; } // Nombre de downvotes plutôt que la liste des upvoters
300+
301+
public CommentDisplayDTO(Comment comment){
302+
Id = comment.Id;
303+
Text = comment.Text;
304+
Author = comment.User.UserName;
305+
Upvotes = comment.Upvoters.Count;
306+
Downvotes = comment.Downvotes.Count;
307+
}
308+
}
309+
```
310+
</TabItem>
311+
</Tabs>
312+
313+
Or, nous pourrions également en profiter pour inclure certaines données qui varient selon l'identité de l'utilisateur :
314+
315+
```cs showLineNumbers
316+
public class CommentDisplayDTO{
317+
318+
public int Id { get; set; }
319+
public string Text { get; set; } = null!;
320+
public string Author { get; set; } = null!;
321+
public int Upvotes { get; set; }
322+
public int Downvotes { get; set; }
323+
324+
public bool IsAuthor { get; set; } // Celui qui envoie la requête est-il l'auteur ?
325+
public bool HasUpvoted { get; set; } // Celui qui envoie la requête a déjà posivoté ?
326+
public bool HasDownvoted { get; set; } // Celui qui envoie la requête a déjà négavoté ?
327+
328+
public CommentDisplayDTO(Comment comment, User user){ // On demande le User en paramètre
329+
Id = comment.Id;
330+
Text = comment.Text;
331+
Author = comment.User.UserName;
332+
Upvotes = comment.Upvoters.Count;
333+
Downvotes = comment.Downvotes.Count;
334+
335+
// Remplir les nouvelles propriétés
336+
IsAuthor = user.UserName == Author;
337+
HasUpvoted = comment.Upvoters.Contains(user);
338+
HasDownvoted = comment.Downvoters.Contains(user);
339+
}
340+
}
341+
```
342+
343+
Avec ces nouvelles propriétés, on pourrait facilement modifier l'apparence d'un commentaire **côté client** :
344+
345+
* Cacher le bouton pour Modifier / Supprimer le commentaire si `IsAuthor` est `false`.
346+
* Changer la couleur des boutons pour upvote / downvote selon la valeur de `HasUpvoted` et `HasDownvoted`.
347+
* Changer la couleur de fond du commentaire si `IsAuthor` est `true`.
348+
* etc.

0 commit comments

Comments
 (0)