隐藏

ASP.NET Core 2.2 WebApi 系列【八】统一返回格式(返回值、模型验证、异常)

发布:2022/4/3 0:45:55作者:管理员 来源:本站 浏览次数:1222



现阶段,基本上都是前后端分离项目,这样一来,就需要前后端配合,没有统一返回格式,那么对接起来会很麻烦,浪费时间。我们需要把所有接口及异常错误信息都返回一定的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 =  };

}


这样需要每个方法都需要重新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 =  };

}

/// <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, ))).ConfigureAwait(false);

}

else

{

var code = context.Response.StatusCode;

switch (code)

{

case :

return;

case :

return;

case :

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 =  };

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(, ErrorMessage = "{0}最多{1}个字符"), Display(Name = "用户Id")]

public string UserId { get; set; }

/// <summary>

/// 用户名

/// </summary>

[StringLength(, ErrorMessage = "{0}最多{1}个字符"), Display(Name = "用户名")]

public string UserName { get; set; }

/// <summary>

/// 邮箱

/// </summary>

[StringLength(, 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 > )

.Select(e1 => e1.Value.Errors.First().ErrorMessage)

.ToList();

var str = string.Join("|", errors);

return new BadRequestObjectResult(ResultModel.Error(str, ));

};

});


两种验证方法效果是一致的