web-dev-qa-db-fra.com

Dans Web API.Core, comment faire la validation de modèle dans chaque méthode?

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

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

/// <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);
    }
}

Ma 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, ma ModelState est bien remplie automatiquement lorsque la connexion a lieu et par exemple. le mot de passe est vide (bien sûr du côté client il devrait y avoir une validation aussi pour cela plus tard).

Ma question est:
Quel est le meilleur moyen de a) vérifier l'état du modèle, b) obtenir une chaîne lisible parmi toutes les erreurs et C) renvoyer un BadRequest avec cette erreur?

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

6
Kovu

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

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

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

Utilisez BadRequest(ModelState) pour renvoyer une réponse à une requête HTTP incorrecte 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 avec une méthode d'assistance ... Mais j'ai peut-être pensé à un filtre?

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 demande.

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é à l'action directement

[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 du modèle dans ASP.NET Core MVC

8
Nkosi

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 de ModelState, vous pouvez les filtrer 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 consiste alors à 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, tous les contrôleurs qui doivent disposer d’une validation automatique de l’état du modèle héritent 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);
        }
    }
}
0
Marcus Höglund