ASP.NET Core Web API中如何为所有方法实现模型验证?
ASP.NET Core 2.0 Web API 登录接口的模型验证最佳实践
我最近在学习ASP.NET Core 2.0 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会自动填充验证错误(比如密码为空的时候),我想知道这三个操作的最佳实现方式:
- 检查模型状态;
- 生成包含所有错误的可读字符串;
- 返回携带该错误信息的
BadRequest。
我自己可以写个助手方法,但又考虑是不是用过滤器来实现更优雅?
两种可行的实现方案
方案一:自定义助手方法(快速简单,适合局部使用)
如果只是少数几个接口需要这个逻辑,写个助手方法就足够了。你可以在控制器里或者单独的静态工具类里实现这个方法:
private string GetModelStateErrorString() { var errors = new List<string>(); foreach (var value in ModelState.Values) { foreach (var error in value.Errors) { errors.Add(error.ErrorMessage); } } return string.Join("; ", errors); }
然后在登录接口里调用:
[AllowAnonymous] [Route("login")] [HttpPost] public IActionResult Login([FromBody]LoginData data) { // 第一步:检查模型状态 if (!ModelState.IsValid) { // 第二步:生成可读错误字符串 var errorMessage = GetModelStateErrorString(); // 第三步:返回带错误的BadRequest return BadRequest(new { Message = "Invalid input", Errors = errorMessage }); } var token = _manager.ValidateCredentialsAndGenerateToken(data); if (token == null) { return Unauthorized(); } else { return Ok(token); } }
这种方式的好处是简单直接,不需要额外配置,适合只在个别接口使用的场景。
方案二:Action过滤器(全局复用,符合DRY原则)
如果你的API里很多接口都需要做模型验证,那用过滤器就更合适了,一次性配置,所有接口自动生效,不用重复写代码。
首先创建一个过滤器类:
public class ModelValidationFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // 检查模型状态 if (!context.ModelState.IsValid) { // 生成可读错误字符串 var errorMessages = context.ModelState.Values .SelectMany(v => v.Errors) .Select(e => e.ErrorMessage) .ToList(); var errorString = string.Join("; ", errorMessages); // 返回BadRequest context.Result = new BadRequestObjectResult(new { Message = "Invalid request data", Errors = errorString }); } } public void OnActionExecuted(ActionExecutedContext context) { // 这里不需要处理 } }
然后在Startup.cs里注册这个过滤器,让它全局生效:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { // 添加全局模型验证过滤器 options.Filters.Add<ModelValidationFilter>(); }); }
这样一来,所有的API接口都会自动检查ModelState,如果验证失败,直接返回带错误信息的BadRequest,你登录接口里的验证代码就可以删掉了,代码会更简洁:
[AllowAnonymous] [Route("login")] [HttpPost] public IActionResult Login([FromBody]LoginData data) { // 现在不用手动检查ModelState了,过滤器已经处理了 var token = _manager.ValidateCredentialsAndGenerateToken(data); if (token == null) { return Unauthorized(); } else { return Ok(token); } }
方案选择建议
- 如果只是个别接口需要验证逻辑,用助手方法就够了,快速省心;
- 如果是多个接口甚至整个API都需要统一的验证错误处理,过滤器是更好的选择,它能帮你减少重复代码,让控制器逻辑更专注于业务本身。
内容的提问来源于stack exchange,提问作者PassionateDeveloper
相关产品推荐
相关产品推荐

