前言
代碼胡亂寫,維護火葬場!前端
在平時工做中遇到前同事寫接口這樣返回值
當接口返回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( _ => "該字段不可爲空。"); })