【问题标题】:How to do model validation in every method in ASP.NET Core Web API?如何在 ASP.NET Core Web API 的每个方法中进行模型验证?
【发布时间】:2018-05-23 06:27:39
【问题描述】:

我正在使用 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);
    }
}

我的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 会在登录发生时自动填充,例如密码为空(当然在客户端也应该稍后验证)。

什么是最好的方法

  • 检查模型状态,
  • 从所有错误中获取可读字符串并
  • 返回 BadRequest 并出现此错误?

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

【问题讨论】:

标签: c# asp.net-core .net-core asp.net-core-webapi modelstate


【解决方案1】:

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

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

【讨论】:

【解决方案2】:

如何查看模型状态?

在action中查看控制器的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);
}

当然,我可以自己用辅助方法编写它……但我可能考虑过一个过滤器?

为避免在需要模型验证的每个操作中重复 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

【讨论】:

  • @Kovu 正如您在帖子中所说,您可以编写一个辅助方法。但我认为过滤器属性更适合这个。因此,您的操作方法不会因重复代码而变得臃肿。只需使用 POST 方法将过滤器全局应用于所有操作。
  • @CodeNotFound 这是一个非常好的建议。模型状态也可以应用于 GET 请求,因此不必仅限于 POST 请求。
  • 确实如此。 ModelState 也可以通过 GET 请求进行验证。但是 GET 请求 URL 大部分时间是由开发人员生成的。因此,如果用户修改了浏览器栏,它将得到 404 Not Found。在大多数情况下,我倾向于避免 MoselState 验证,因为大多数这些类型的请求使用简单的参数(通常是值类型),所以我使用路由约束。如果不遵守约束,它会自动重定向到 404。
  • @CodeNotFound 我想我应该澄清一下,我指的是具有数据注释的复杂模型。我明白你的意思。它们是有效的。 :)
【解决方案3】:

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

ModelState.IsValid

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

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

然后一个选项是验证每个方法/控制器中的状态,但我建议您在验证模型的基类中实现验证
像这样的 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);
    }
}

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

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

【讨论】:

  • 要从 ModelState 中获取错误,您可以从字典中过滤掉错误并将它们作为列表返回 在极少数情况下,有人需要这样做 :)大多数情况下只需BadRequest(ModelState);new BadRequestObjectResult(context.ModelState);
  • @CodeNotFound 感谢您提供的信息。我想我的列表是一个极端情况,我使用 SelectMany 来获得一个只有错误的扁平数组。
猜你喜欢
  • 1970-01-01
  • 2023-03-23
  • 2019-03-26
  • 2019-03-16
  • 1970-01-01
  • 2017-04-11
  • 1970-01-01
  • 1970-01-01
  • 2021-05-18
相关资源
最近更新 更多