ASP.NET Core 2.2 WebApi 系列【八】統一返回格式(返回值、模型驗證、異常)


現階段,基本上都是前后端分離項目,這樣一來,就需要前后端配合,沒有統一返回格式,那么對接起來會很麻煩,浪費時間。我們需要把所有接口及異常錯誤信息都返回一定的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;
                });

參考:https://docs.microsoft.com/zh-cn/dotnet/api/microsoft.aspnetcore.mvc.apibehavioroptions?view=aspnetcore-2.2

自定義過濾器

創建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 JsonResult(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 JsonResult(ResultModel.Error(str, 400));
                    };
                });

兩種驗證方法效果是一致的


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM