如何在 ASP.NET Core Web API 中的每个方法中进行模型验证?

How to do model validation in every method in ASP.NET Core Web API?

我正在进入 ASP.NET Core 2.0 with Web API。我的第一个方法是登录:

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

我的 LoginData 使用 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; }
}

所以我的 ModelState 会在登录时自动填充,例如密码为空(当然在客户端稍后也应该进行验证)。

最好的方法是什么

当然我可以用辅助方法自己写。但我考虑过过滤器吗?

How to check the model state?

在动作中查看控制器的ModelState以获取模型的状态。

getting a readable string out of all errors and return a BadRequest with this error?

使用 BadRequest(ModelState) 到 return HTTP 错误请求响应,它将检查模型状态并使用错误构造消息。

完成代码

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

Of course I could write it all myself in a helper method... But I thought about a filter maybe?

为了避免在需要模型验证的每个操作中重复 ModelState.IsValid 代码,您可以创建一个过滤器来检查模型状态并使请求短路。

例如

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

可以直接应用于动作

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

或全局添加以应用于应检查模型状态的所有请求。

引用Model validation in ASP.NET Core MVC

要检查模型状态是否有效,请使用 ModelState 属性(由控制器 class 继承自的 ControllerBase class 公开)

ModelState.IsValid

要从 ModelState 中获取错误,您可以从字典中过滤出错误,然后return将它们作为列表

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

然后,一个选项是验证每个 method/controller 中的状态,但我建议您在基础 class 中实施验证,该基础验证
中的模型 像这样的 OnActionExecuting 方法

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

然后每个应该具有自动模型状态验证的控制器都继承自基础class

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

我强烈建议使用 [ApiController] 和其他有助于在基于 Web API 的项目中简化验证的属性。

[ApiController] 此属性在进入方法之前为您完成模态框的所有基本验证。因此,如果您想进行某种形式的自定义验证,则只需检查模态。