J'entre dans ASP.Net Core 2.0 avec l'API Web.

Une de mes premières méthodes est mon login:

/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data)
{
    var token = _manager.ValidateCredentialsAndGenerateToken(data);
    if (token == null)
    {
        return Unauthorized();
    }
    else
    {
        return Ok(token);
    }
}

Mon LoginData utilisant DataAnnotations:

public class LoginData
{
    [Required]
    [MaxLength(50)]
    public string Username { get; set; }

    [Required]
    public string Password { get; set; }

    [Required]
    [MaxLength(16)]
    public string IpAddress { get; set; }
}

Donc, mon ModelState est bien rempli automatiquement lorsque la connexion se produit et par exemple le mot de passe est vide (bien sûr côté client il devrait y avoir une validation aussi pour cela plus tard).

Ma question est:
Quelle est la meilleure façon de a) vérifier l'état du modèle, b) obtenir une chaîne lisible de toutes les erreurs et C) renvoyer une BadRequest avec cette erreur?

Bien sûr, je pourrais tout écrire moi-même dans une méthode d'aide ... Mais j'ai pensé à un filtre peut-être?

13
Kovu 23 mai 2018 à 09:27

3 réponses

Meilleure réponse

Comment vérifier l'état du modèle?

Vérifiez le ModelState du contrôleur dans l'action pour obtenir l'état du modèle.

obtenir une chaîne lisible de toutes les erreurs et renvoyer une BadRequest avec cette erreur?

Utilisez BadRequest(ModelState) pour renvoyer une mauvaise réponse de requête HTTP qui inspectera l'état du modèle et construira le message à l'aide d'erreurs.

Code complété

/// <summary>
/// API endpoint to login a user
/// </summary>
/// <param name="data">The login data</param>
/// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
    if(ModelState.IsValid) {
        var token = _manager.ValidateCredentialsAndGenerateToken(data);
        if (token == null) {
            return Unauthorized();
        } else {
            return Ok(token);
        }
    }
    return BadRequest(ModelState);
}

Bien sûr, je pourrais tout écrire moi-même dans une méthode d'aide ... Mais j'ai pensé à un filtre peut-être?

Pour éviter le code ModelState.IsValid répété dans chaque action où la validation du modèle est requise, vous pouvez créer un filtre pour vérifier l'état du modèle et court-circuiter la requête.

Par exemple

public class ValidateModelAttribute : ActionFilterAttribute {
    public override void OnActionExecuting(ActionExecutingContext context) {
        if (!context.ModelState.IsValid) {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

Peut être appliqué directement à l'action

[ValidateModel] //<-- validation
[AllowAnonymous]
[Route("login")]
[HttpPost]
public IActionResult Login([FromBody]LoginData data) {
    var token = _manager.ValidateCredentialsAndGenerateToken(data);
    if (token == null) {
        return Unauthorized();
    } else {
        return Ok(token);
    }    
}

Ou ajouté globalement pour être appliqué à toutes les demandes où l'état du modèle doit être vérifié.

Référence Validation de modèle dans ASP.NET Core MVC

23
Nkosi 23 mai 2018 à 11:36

Pour vérifier si l'état du modèle est valide, utilisez la propriété ModelState (exposée par la classe ControllerBase dont la classe Controller hérite)

ModelState.IsValid

Pour obtenir les erreurs du ModelState, vous pouvez filtrer les erreurs du dictionnaire et les renvoyer sous forme de liste

var errors = ModelState
    .Where(a => a.Value.Errors.Count > 0)
    .SelectMany(x => x.Value.Errors)
    .ToList();

Une option est alors de valider l'état dans chaque méthode / contrôleur mais je vous recommande d'implémenter la validation dans une classe de base qui valide le modèle dans le
Méthode OnActionExecuting comme celle-ci

public class ApiController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!ModelState.IsValid)
        {
            var errors = ModelState
                .Where(a => a.Value.Errors.Count > 0)
                .SelectMany(x => x.Value.Errors)
                .ToList();
            context.Result = new BadRequestObjectResult(errors);
        }
        base.OnActionExecuting(context);
    }
}

Ensuite, chaque contrôleur qui devrait avoir une validation automatique de l'état du modèle hérite simplement de la classe de base

public class TokenController : ApiController
{
    /// <summary>
    /// API endpoint to login a user
    /// </summary>
    /// <param name="data">The login data</param>
    /// <returns>Unauthorizied if the login fails, The jwt token as string if the login succeded</returns>
    [AllowAnonymous]
    [Route("login")]
    [HttpPost]
    public IActionResult Login([FromBody]LoginData data)
    {
        var token = _manager.ValidateCredentialsAndGenerateToken(data);
        if (token == null)
        {
            return Unauthorized();
        }
        else
        {
            return Ok(token);
        }
    }
}
5
Marcus Höglund 23 mai 2018 à 11:43

Je recommande vivement d'utiliser [ApiController] et d'autres attributs qui facilitent la validation dans les projets basés sur l'API Web.

[ApiController] cet attribut fait toute la validation de base sur le modal pour vous avant qu'il n'entre dans la méthode. Il vous suffit donc d'inspecter le modal si vous souhaitez effectuer une forme de validation personnalisée.

8
Hawkzey 12 juin 2019 à 16:01