在Web API.Core中,如何在每种方法中进行模型验证?

Kov*_*ovu 10 c# modelstate .net-core asp.net-core asp.net-core-webapi

我正在使用Web API进入ASP.Net Core 2.0.

我的第一个方法之一是我的登录:

/// <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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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; }
}
Run Code Online (Sandbox Code Playgroud)

因此,ModelState当登录发生时,我的信息会自动填满,例如密码为空(当然在客户端也应该对其进行验证).

我的问题是:
a)检查模型状态的最佳方法是什么,b)从所有错误中获取可读字符串并且C)返回带有此错误的BadRequest?

当然我可以用辅助方法自己写出来......但我想过一个过滤器可能吗?

Nko*_*osi 18

如何检查模型状态?

检查操作中的控制器ModelState以获取模型的状态.

从所有错误中获取可读字符串并返回带有此错误的BadRequest?

使用BadRequest(ModelState)返回将检查模型的状态,并使用错误的构造消息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);
}
Run Code Online (Sandbox Code Playgroud)

当然我可以用辅助方法自己写出来......但我想过一个过滤器可能吗?

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

例如

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

可以直接应用于动作

[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);
    }    
}
Run Code Online (Sandbox Code Playgroud)

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

ASP.NET Core MVC中的参考模型验证

  • @Kovu正如你在帖子中所说,你可以写一个帮助方法.但我认为过滤器attrribute更适合这个.因此,您的操作方法不会因重复代码而变得臃肿.只需使用POST方法将过滤器全局应用于所有操作. (2认同)
  • 确实.ModelState也可以通过GEt请求进行验证.但是GET请求URL大部分是由开发人员生成的.因此,如果用户修改了浏览器栏,它将获得404 Not Found.在大多数情况下,我倾向于避免MoselState验证,因为大多数类型的请求使用简单参数(通常是值类型),所以我使用路由约束.如果不遵守约束,则会自动将其重定向到404. (2认同)

Mar*_*und 7

要检查模型状态是否有效,请使用 ModelState 属性(由 Controller 类继承的 ControllerBase 类公开)

ModelState.IsValid
Run Code Online (Sandbox Code Playgroud)

要从 ModelState 中获取错误,您可以从字典中过滤掉错误并将它们作为列表返回

var errors = ModelState
    .Where(a => a.Value.Errors.Count > 0)
    .SelectMany(x => x.Value.Errors)
    .ToList();
Run Code Online (Sandbox Code Playgroud)

一种选择是验证每个方法/控制器中的状态,但我建议您在基类中实现验证,该基类验证
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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • _要从 ModelState 中获取错误,您可以从字典中过滤掉错误并将它们作为列表返回_在极少数情况下,有人需要这样做:) 只需 `BadRequest(ModelState);` 或 `new BadRequestObjectResult(context. ModelState);`对于大多数情况 (2认同)

Haw*_*zey 5

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

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

  • 对于 ASP.NET Core 2.1 及更高版本,这应该是可接受的答案。 (4认同)