前言
代码胡乱写,维护火葬场!前端
在平时工做中遇到前同事写接口这样返回值
当接口返回1时,不去看他的代码就永远猜不到这个1表明的是返回成功仍是返回值
json
稍微好点的
维护和改bug简直让人疯狂,致使大部分时间浪费在“体会”别人返回值的逻辑中
后端
每天加班与救bug于水火之中
安全
合理分配先后端返回值很重要!
通常咱们经常使用统一的格式返回json,不管后台是运行正常仍是发生异常,响应给前端的数据格式是不变的!app
public class Result<T> where T : class { public ResultCode code { get; set; } public string msg { get; set; } public T data { get; set; } }
包装一下,一般使用构造函数async
public class Result<T> where T : class { public ResultCode code { get; set; } public string msg { get; set; } public T data { get; set; } public Result(T data) { code = ResultCode.Ok; msg = ResultCode.Ok.GetDescription(); this.data = data; } public Result(ResultCode code, string msg, T data) { this.code = code; this.msg = msg ?? code.GetDescription(); this.data = data; } }
这么使用new Result<Store>(code, msg, data)
使用起来仍是不够简化
继续封装
ide
public class Result<T> where T : class { public ResultCode code { get; set; } public string msg { get; set; } public T data { get; set; } public Result(T data) { code = ResultCode.Ok; msg = ResultCode.Ok.GetDescription(); this.data = data; } public Result(ResultCode code, string msg, T data) { this.code = code; this.msg = msg ?? code.GetDescription(); this.data = data; } public static Result<T> FromCode(ResultCode code, string msg = null, T data = default) { if (data == null) data = (T) new Object(); return new Result<T>(code, msg, data); } public static Result<T> Ok(T data) { return new Result<T>(data); } public static Result<T> FromError(ResultCode code = ResultCode.Fail, string msg = null, T data = default) { return new Result<T>(code, msg, data); } }
[HttpGet] public Result<Store> Get() { return Result<Store>.Ok(new Store()); }
这样就稍微好点了函数
全局处理响应数据
虽然控制器返回值能够统一了,可是异常处理并无统一,这时候就须要建个中间件/异常过滤器进行处理异常ui
app.UseMiddleware<GlobalExceptionHandlerMiddleware>(); public class GlobalExceptionHandlerMiddleware { private readonly RequestDelegate _next; public GlobalExceptionHandlerMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (System.Exception ex) { context.Response.StatusCode = 500; context.Response.ContentType = "text/json;charset=utf-8;"; if (ex is ResultException == false) { var logger = context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger<GlobalExceptionHandlerMiddleware>(); logger.LogError(1, ex, ex.Message); } var json = Result<object>.FromCode(ResultCode.Fail, ex.Message); var error = JsonSerializer.Serialize(json); await context.Response.WriteAsync(error); } } }
有些异常是不须要记录的,这时候能够自定义ResultException
进行判断this
public class ResultException : Exception { public ResultException(string message) : base(message) { } public ResultException(string message, Exception e) : base(message, e) { } } if (ex is ResultException == false) { var logger = context.RequestServices.GetRequiredService<ILoggerFactory>().CreateLogger<GlobalExceptionHandlerMiddleware>(); logger.LogError(1, ex, ex.Message); }
参数校验处理
一个接口通常对参数(请求数据)都会进行安全校验,按照以前的格式返回验证异常。
public class ModelActionFilter : ActionFilterAttribute, IActionFilter { public override void OnActionExecuting(ActionExecutingContext context) { if (!context.ModelState.IsValid) { var errorResults = new List<ErrorResultDto>(); foreach (var item in context.ModelState) { var result = new ErrorResultDto { Field = item.Key, Msg = "", }; foreach (var error in item.Value.Errors) { if (!string.IsNullOrEmpty(result.Msg)) { result.Msg += '|'; } result.Msg += error.ErrorMessage; } errorResults.Add(result); } context.Result = new JsonResult(Result<List<ErrorResultDto>>.FromCode(ResultCode.InvalidData, data: errorResults)); } } } public class ErrorResultDto { /// <summary> /// 参数领域 /// </summary> public string Field { get; set; } /// <summary> /// 错误信息 /// </summary> public string Msg { get; set; } }
Startup.cs
services.AddControllers(options => { options.Filters.Add(new ModelActionFilter()); options.MaxModelValidationErrors = 50; options.ModelBindingMessageProvider.SetValueMustNotBeNullAccessor( _ => "该字段不可为空。"); })