fix/issue-3-splitquery #5
3 changed files with 104 additions and 1 deletions
Blog: render user avatar through a null-safe helper
GET /BlogSpot/Details/1 retournait 500 (avec un 500-sur-500 sur
la page d'erreur elle-même) parce que DisplayTemplates
/ApplicationUser.cshtml faisait `var avuri = "/Avatars/" +
Model.UserName + ".s.png"` : avec <Nullable>enable</Nullable>,
Razor émet un null-check implicite sur Model.UserName et lève
NullReferenceException quand l'auteur n'a pas de UserName
posé (donnée héritée, user partiellement initialisé).
- UserDisplayHelpers.AvatarSrc : helper statique pur dans
Yavsc.Abstract.Identity qui retourne
YavscConstants.DefaultAvatar pour user null / UserName vide
ou whitespace, et un path /avatars/<name>.s.png sinon.
- ApplicationUser.cshtml : utilise le helper.
- Tests : 4 cas (null, vide, whitespace, valide) dans
Yavsc.Org.Tests/NonRegression.
Bonus : le path d'avatar passe de "/Avatars/" (S majuscule,
ne résolvait pas dans le middleware de fichiers statiques) à
YavscConstants.AvatarsPath ("/avatars" minuscule), pour fermer
l'autre trou que centraliser le calcul permettait de fixer
proprement.
commit
bbdcc7f2ad
36
src/Yavsc.Abstract/Identity/UserDisplayHelpers.cs
Normal file
36
src/Yavsc.Abstract/Identity/UserDisplayHelpers.cs
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
namespace Yavsc.Abstract.Identity
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helpers Razor-friendly pour rendre un user dans une vue
|
||||||
|
/// sans exposer le template à des accesseurs nullables qui
|
||||||
|
/// lèveraient <see cref="System.NullReferenceException"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class UserDisplayHelpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Chemin d'avatar à utiliser pour <paramref name="user"/>
|
||||||
|
/// dans un display template. Défense contre <c>null</c>
|
||||||
|
/// (user pas chargé, FK orpheline) et contre un
|
||||||
|
/// <c>UserName</c> vide (donnée héritée, user partiellement
|
||||||
|
/// initialisé). Sans cette garde, Razor émet une
|
||||||
|
/// NullReferenceException dès qu'un accesseur <c>.UserName</c>
|
||||||
|
/// apparaît dans le template, ce qui remonte en 500 et
|
||||||
|
/// masque la page d'erreur elle-même.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Le path retourné est aligné sur
|
||||||
|
/// <see cref="YavscConstants.AvatarsPath"/> (minuscule).
|
||||||
|
/// Les anciens display templates utilisaient "/Avatars/"
|
||||||
|
/// avec un S majuscule, en désaccord avec le path statique
|
||||||
|
/// servi par le middleware de fichiers — les images ne
|
||||||
|
/// résolvaient pas. Centraliser le calcul ici ferme les
|
||||||
|
/// deux trous.
|
||||||
|
/// </remarks>
|
||||||
|
public static string AvatarSrc(IApplicationUser? user)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(user?.UserName))
|
||||||
|
return YavscConstants.DefaultAvatar;
|
||||||
|
return $"{YavscConstants.AvatarsPath}/{user!.UserName}.s.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
src/Yavsc.Org.Tests/NonRegression/UserDisplayHelpersTests.cs
Normal file
63
src/Yavsc.Org.Tests/NonRegression/UserDisplayHelpersTests.cs
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
using Xunit;
|
||||||
|
using Yavsc.Abstract;
|
||||||
|
using Yavsc.Abstract.Identity;
|
||||||
|
|
||||||
|
namespace Yavsc.Org.Tests.NonRegression;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Régression sur le 500 <c>GET /BlogSpot/Details/{id}</c> : un
|
||||||
|
/// billet dont l'auteur a un <c>UserName</c> null ou absent faisait
|
||||||
|
/// lever <c>NullReferenceException</c> dans le display template
|
||||||
|
/// <c>ApplicationUser.cshtml</c> à l'évaluation de
|
||||||
|
/// <c>Model.UserName</c>. ASP.NET transforme en 500, et la page
|
||||||
|
/// d'erreur elle-même crash (ErrorViewModel manquant), donc on
|
||||||
|
/// ne voit rien — juste un 500 muet.
|
||||||
|
///
|
||||||
|
/// Le fix passe par <see cref="UserDisplayHelpers.AvatarSrc"/> qui
|
||||||
|
/// retourne <see cref="YavscConstants.DefaultAvatar"/> pour toute
|
||||||
|
/// donnée partielle. Ces tests couvrent les trois formes de
|
||||||
|
/// "donnée absente" : user null, UserName vide, UserName whitespace.
|
||||||
|
/// </summary>
|
||||||
|
public class UserDisplayHelpersTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void AvatarSrc_null_user_returns_default_avatar()
|
||||||
|
{
|
||||||
|
Assert.Equal(YavscConstants.DefaultAvatar, UserDisplayHelpers.AvatarSrc(null));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AvatarSrc_user_with_empty_UserName_returns_default_avatar()
|
||||||
|
{
|
||||||
|
var user = new FakeUser { UserName = "" };
|
||||||
|
Assert.Equal(YavscConstants.DefaultAvatar, UserDisplayHelpers.AvatarSrc(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AvatarSrc_user_with_whitespace_UserName_returns_default_avatar()
|
||||||
|
{
|
||||||
|
var user = new FakeUser { UserName = " " };
|
||||||
|
Assert.Equal(YavscConstants.DefaultAvatar, UserDisplayHelpers.AvatarSrc(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AvatarSrc_valid_user_returns_canonical_avatars_path()
|
||||||
|
{
|
||||||
|
var user = new FakeUser { UserName = "alice" };
|
||||||
|
// Le path doit matcher YavscConstants.AvatarsPath (minuscule),
|
||||||
|
// pas un /Avatars/ avec S majuscule qui ne résout pas
|
||||||
|
// dans le middleware de fichiers statiques.
|
||||||
|
var expected = $"{YavscConstants.AvatarsPath}/alice.s.png";
|
||||||
|
Assert.Equal(expected, UserDisplayHelpers.AvatarSrc(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class FakeUser : IApplicationUser
|
||||||
|
{
|
||||||
|
public string Id { get; set; } = "";
|
||||||
|
public string? UserName { get; set; }
|
||||||
|
public string? Avatar { get; set; }
|
||||||
|
public IAccountBalance? AccountBalance => null;
|
||||||
|
public string? DedicatedGoogleCalendar => null;
|
||||||
|
public ILocation? PostalAddress => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
@using Yavsc.Abstract.Identity
|
@using Yavsc.Abstract.Identity
|
||||||
@model ApplicationUser
|
@model ApplicationUser
|
||||||
@{
|
@{
|
||||||
var avuri = "/Avatars/" + Model.UserName + ".s.png";
|
// Le helper défend contre Model null et contre UserName vide
|
||||||
|
// ou whitespace. Sans cette garde, Razor lève
|
||||||
|
// NullReferenceException ici, ce qui propage un 500 et
|
||||||
|
// masque aussi la page d'erreur.
|
||||||
|
var avuri = UserDisplayHelpers.AvatarSrc(Model);
|
||||||
}
|
}
|
||||||
<div class="userinfo">
|
<div class="userinfo">
|
||||||
<a title="Posts" asp-controller="Blogspot" asp-action="Index" asp-route-id="@Model.UserName" class="btn btn-primary">
|
<a title="Posts" asp-controller="Blogspot" asp-action="Index" asp-route-id="@Model.UserName" class="btn btn-primary">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue