dotNETCoreWebAPI统⼀处理(返回值、参数验证、异常)
这⾥写⾃定义⽬录标题
dotNET Core WebAPI 统⼀处理(返回值、参数验证、异常)
现在 Web 开发⽐较流⾏前后端分离,我们的产品也是⼀样,前端使⽤Vue,后端使⽤ dotNet Core WebAPI ,在写 API 的过程中有很多地⽅需要统⼀处理:
⽂档
参数验证
返回值
异常处理
本⽂就说说 API 的统⼀处理这些事。
环境
dotNet Core:2.1
VS For Mac:8.1
⽂档
Swagger 是⼀个 API ⽂档⽣成框架,在⾮ Core 时代就⼀直在使⽤,现在前后端分离的模式下,API ⽂档更是⾮常重要,让前端开发⼈员和后端开发⼈员能更好的沟通和合作,前端开发⼈员在 Swagger 可以了解到接⼝的地址、⼊参、出参,还能模拟调⽤,⾮常⽅便。
安装
在 VS For Mac 中创建 API 项⽬ DotNetCoreApiSample ,在依赖项中的 NuGet 上点击右键,选择添加包,如下图:
搜索 Swashbuckle.AspNetCore,选中搜索结果的第⼀条,点击「添加包」按钮进⾏添加。
配置
Startup 类的 ConfigureServices ⽅法中添加
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(“v1”, new Swashbuckle.AspNetCore.Swagger.Info
{
Version = “v1”,
Title = “DotNet Core WebAPI⽂档”
});
});
Startup 类的 Configure ⽅法中添加
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", “DotNet Core WebAPI⽂档”);
});
运⾏效果
参数验证
此处所说的参数验证指的是实体类型的参数验证,通过在实体的属性上添加特性的⽅式来实现。简单实现
创建名为 ValidationDemoController 的 API 类,代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
validation框架namespace DotNetCoreApiSample.Controllers
{
[Route(“api/[controller]”)]
public class ValidationDemoController : Controller
{
[HttpPost]
public IActionResult AddUser([FromBody]User user)
{
string errorMessage = string.Empty;
if (!ModelState.IsValid)
{
foreach (var item in ModelState.Values)
{
foreach (var error in item.Errors)
{
errorMessage += error.ErrorMessage + “|”;
}
}
}
if(!string.IsNullOrEmpty(errorMessage))
{
return BadRequest(errorMessage);
}
return Ok();
}
}
public class User
{
[Required(ErrorMessage = "⽤户Code不能为空")]
public string Code { get; set; }
[Required(ErrorMessage = "⽤户名称不能为空")]
public string Name { get; set; }
[Required(ErrorMessage = "⽤户年龄不能为空")]
[Range(1, 100, ErrorMessage = "年龄必须介于1~100之间")]
public int Age { get; set; }
public string Address { get; set; }
}
}
实体类属性使⽤ Required 等特性需要引⽤命名空间System.ComponentModel.DataAnnotations
除了上⾯的 Required 和 Range 标记,还有很多实⽤的标记,详细参考:msdn.microsoft/en-us/library/systemponentmodel.dataannotations( v=vs.110).aspx.aspx)
上⾯的⽰例代码将错误信息的收集写在了接⼝⽅法中,这是⼀个很不好的做法,仅仅实现了功能,下⾯将通过过滤器的⽅式来进⾏重构,统⼀处理错误信息
重构
添加名为 ValidateModelAttribute 的过滤器类,继承 ActionFilterAttribute ,代码如下
namespace DotNetCoreApiSample.Filters
{
public class ValidateModelAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (!context.ModelState.IsValid)
{
var result = context.ModelState.Keys
.SelectMany(key => context.ModelState[key].Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
.ToList();
context.Result = new ObjectResult(result);
}
}
}
public class ValidationError
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public string Field { get; }
public string Message { get; }
public ValidationError(string field, string message)
{
Field = field != string.Empty ? field : null;
Message = message;
}
}
}
Startup 类的 ConfigureServices ⽅法中添加下⾯代码:
services.AddMvc(options =>
{
options.Filters.Add();
});
使⽤ Postman 调⽤结果如下
返回值
返回值的统⼀处理需要下⾯⼏个步骤:
创建统⼀返回结果的实体类,所有的接⼝⽅法都返回固定格式,⽅便前端统⼀处理
创建过滤器,过滤器⽤来拦截请求,包装结果,统⼀输出
Startup 类中进⾏配置注册
结果实体类
接⼝的返回值需要统⼀的格式,下⾯的属性字段是我认为必须要有的
Result:返回的结果
Message:出现错误或需要提⽰时的提⽰⽂本内容
Code:调⽤成功、失败或出错时的编码
ReturnStatus:⽤来判断接⼝调⽤状态的
创建返回结果的实体类 BaseResultModel
public class BaseResultModel
{
public BaseResultModel(int? code = null, string message = null,
object result = null, ReturnStatus returnStatus = ReturnStatus.Success)
{
this.Code = code;
this.Result = result;
this.Message = message;
this.ReturnStatus = returnStatus;
}
public int? Code { get; set; }
public string Message { get; set; }
public object Result { get; set; }
public ReturnStatus ReturnStatus { get; set; }
}
public enum ReturnStatus
{
Success = 1,
Fail = 0,
ConfirmIsContinue = 2,
Error = 3
}
过滤器类
创建名称为 ApiResultFilterAttribute 的过滤器类,该类继承 ActionFilterAttribute ,具体代码如下
public class ApiResultFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
base.OnActionExecuting(context);
}
public override void OnResultExecuting(ResultExecutingContext context)
{
var objectResult = context.Result as ObjectResult;
context.Result = new OkObjectResult(new BaseResultModel(code:200, result: objectResult.Value)); }
}
在过滤器中将接⼝的返回值获取后重新包装到 BaseResultModel 模型类中进⾏返回。
Startup 配置
在 Startup 类的 ConfigureServices ⽅法中添加如下代码
services.AddMvc(options =>
{
options.Filters.Add();
options.Filters.Add();
});
添加⽰例接⼝⽅法
[HttpGet]
public IActionResult GetUserCode()
{
return Ok(“oec2003”);
}
运⾏效果
使⽤ Postman 调⽤该接⼝⽅法,返回结果如下
继续重构参数验证
添加了返回值的过滤器类后,调⽤之前的参数验证的接⼝,会发现返回结果如下
{
“code”: 200,
“message”: null,
“result”: [
{
“field”: “Age”,
“message”: “年龄必须介于1~100之间”
}
],
“returnStatus”: 1
}
接⼝会调⽤两次过滤器,先调⽤参数验证的过滤器,再调⽤返回值的过滤器,导致验证失败的接⼝返回值状态也是成功的,所以需要做进⼀步重构。
1、添加 ValidationFailedResultModel 类
public class ValidationFailedResultModel : BaseResultModel
{
public ValidationFailedResultModel(ModelStateDictionary modelState)
{
Code = 422;
Message = “参数不合法”;
Result = modelState.Keys
.SelectMany(key => modelState[key].Errors.Select(x => new ValidationError(key, x.ErrorMessage)))
.ToList();
ReturnStatus = ReturnStatus.Fail;
}
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论