現階段,基本上都是前後端分離項目,這樣一來,就需要前後端配合,沒有統一返回格式,那麼對接起來會很麻煩,浪費時間。我們需要把所有介面及異常錯誤信息都返回一定的Json格式,有利於前端處理,從而提高了工作效率。 一、準備工作 定義響應實體類 /// <summary> /// 響應實體類 /// </s ...
現階段,基本上都是前後端分離項目,這樣一來,就需要前後端配合,沒有統一返回格式,那麼對接起來會很麻煩,浪費時間。我們需要把所有介面及異常錯誤信息都返回一定的Json格式,有利於前端處理,從而提高了工作效率。
一、準備工作
定義響應實體類
/// <summary> /// 響應實體類 /// </summary> public class ResultModel { /// <summary> /// 狀態碼 /// </summary> public int ReturnCode { get; set; } /// <summary> /// 內容 /// </summary> public object Data { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string ErrorMessage { get; set; } /// <summary> /// 是否成功 /// </summary> public bool IsSuccess { get; set; } }
修改Controller層
在controller層處理業務請求,new 一個ResultModel 對象,返回給前端。
/// <summary> /// 查詢用戶 /// </summary> /// <returns></returns> [Route("getUser")] [HttpGet] public ResultModel GetUser() { var data = _userRepository.GetAll().ToList(); return new ResultModel() { Data = data, ErrorMessage = null, IsSuccess = true, ReturnCode = 200 }; }
這樣需要每個方法都需要重新new一個ResultModel 對象,感覺有點代碼冗餘。
我們只需要加幾個靜態方法,每個方法返回都是ResultModel對象,成功的時候調用ResultModel.OK,失敗的時候調用ResultModel.ERROR即可。
優化
添加兩個靜態方法
/// <summary> /// 成功 /// </summary> /// <param name="data">返回數據</param> /// <returns></returns> public static ResultModel Ok(object data) { return new ResultModel { Data = data, ErrorMessage = null, IsSuccess = true, ReturnCode = 200 }; } /// <summary> /// 失敗 /// </summary> /// <param name="str">錯誤信息</param> /// <param name="code">狀態碼</param> /// <returns></returns> public static ResultModel Error(string str,int code) { return new ResultModel { Data = null, ErrorMessage = str, IsSuccess = false, ReturnCode = code }; }
修改控制器
/// <summary> /// 查詢用戶 /// </summary> /// <returns></returns> [Route("getUser")] [HttpGet] public ResultModel GetUser() { var data = _userRepository.GetAll().ToList(); return ResultModel.Ok(data); }
二、全局異常處理
通過全局異常處理,任何錯誤信息都會被攔截,返回統一格式。
定義全局異常處理中間件
using System; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using NetCoreWebApi.Util; using Newtonsoft.Json; namespace NetCoreWebApi.Filter { /// <summary> /// 處理全局信息中間件 /// </summary> public class ExceptionMiddleWare { /// <summary> /// 處理HTTP請求的函數。 /// </summary> private readonly RequestDelegate _next; /// <summary> /// 構造函數 /// </summary> /// <param name="next"></param> public ExceptionMiddleWare(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context) { try { //拋給下一個中間件 await _next(context); } catch (Exception ex) { await WriteExceptionAsync(context, ex); } finally { await WriteExceptionAsync(context, null); } } private async Task WriteExceptionAsync(HttpContext context, Exception exception) { if (exception != null) { var response = context.Response; var message = exception.InnerException == null ? exception.Message : exception.InnerException.Message; response.ContentType = "application/json"; await response.WriteAsync(JsonConvert.SerializeObject(ResultModel.Error(message, 400))).ConfigureAwait(false); } else { var code = context.Response.StatusCode; switch (code) { case 200: return; case 204: return; case 401: context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject(ResultModel.Error("token已過期,請重新登錄.", code))).ConfigureAwait(false); break; default: context.Response.ContentType = "application/json"; await context.Response.WriteAsync(JsonConvert.SerializeObject(ResultModel.Error("未知錯誤", code))).ConfigureAwait(false); break; } } } } }
註冊中間件
在Startup.cs啟動類的Configure方法中添加UseMiddleware方法
//異常處理中間件 app.UseMiddleware(typeof(ExceptionMiddleWare));
三、驗證實體模型
有兩種方式:
1.使用自定義過濾器
2.使用預設自帶的400模型驗證,需要在控制器上面加上【ApiController】,這種方式優先順序比較高,如果需要自定義模型驗證,則需要先關閉預設的模型驗證
【ApiController】
自動推斷參數綁定:可以省略[FromBody]等參數特性
自動模型驗證:自動驗證模型是否合法
參考:https://blog.csdn.net/sD7O95O/article/details/81844154
services.AddMvc(e => { e.Filters.Add<CheckModel>(); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .ConfigureApiBehaviorOptions(e => { //關閉預設模型驗證 e.SuppressModelStateInvalidFilter = true; });
自定義過濾器
創建CheckModel過濾器繼承ActionFilterAttribute抽象類,重寫其中需要的方法
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using NetCoreWebApi.Util; namespace NetCoreWebApi.Filter { /// <summary> /// 驗證實體對象是否合法 /// </summary> public class CheckModel : ActionFilterAttribute { /// <summary> /// Action 調用前執行 /// </summary> /// <param name="actionContext"></param> public override void OnActionExecuting(ActionExecutingContext actionContext) { if (!actionContext.ModelState.IsValid) { //初始化返回結果 var result = new ResultModel { IsSuccess = false, ReturnCode = 400 }; foreach (var item in actionContext.ModelState.Values) { foreach (var error in item.Errors) { result.ErrorMessage += error.ErrorMessage + "|"; } } actionContext.Result = new BadRequestObjectResult(result); } } /// <summary> /// Action 方法調用後,Result 方法調用前執行 /// </summary> /// <param name="context"></param> public override void OnActionExecuted(ActionExecutedContext context) {
} } }
將寫的過濾器註冊到全局
services.AddMvc(e => { //註冊過濾器 e.Filters.Add<CheckModel>(); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .ConfigureApiBehaviorOptions(e => { //關閉預設模型驗證 e.SuppressModelStateInvalidFilter = true; });
創建UserDto
using System.ComponentModel.DataAnnotations; namespace NetCoreWebApi.Repository.Dto { /// <summary> /// 用戶傳輸對象 /// </summary> public class UserDto { /// <summary> /// 用戶Id /// </summary> [StringLength(32, ErrorMessage = "{0}最多{1}個字元"), Display(Name = "用戶Id")] public string UserId { get; set; } /// <summary> /// 用戶名 /// </summary> [StringLength(20, ErrorMessage = "{0}最多{1}個字元"), Display(Name = "用戶名")] public string UserName { get; set; } /// <summary> /// 郵箱 /// </summary> [StringLength(30, ErrorMessage = "{0}最多{1}個字元"), Display(Name = "郵箱")] public string Email { get; set; } } }
測試
預設模型驗證
services.AddMvc(e => { //註冊過濾器 //e.Filters.Add<CheckModel>(); }) .SetCompatibilityVersion(CompatibilityVersion.Version_2_2) .ConfigureApiBehaviorOptions(e => { ////關閉預設模型驗證 //e.SuppressModelStateInvalidFilter = true; e.InvalidModelStateResponseFactory = actionContext => { //獲取驗證失敗的模型欄位 var errors = actionContext.ModelState .Where(e1 => e1.Value.Errors.Count > 0) .Select(e1 => e1.Value.Errors.First().ErrorMessage) .ToList(); var str = string.Join("|", errors); return new BadRequestObjectResult(ResultModel.Error(str, 400)); }; });
兩種驗證方法效果是一致的