.NET实现统一接口数据返回格式
.NET实现统一接口数据返回格式
前言
在学习了博客系统后,发现画星星博主写的统一接口返回格式类非常滴好用,这里就讲解一下如何使用他写的规范。
合法的接口返回值应该包括状态码、提示信息、和数据
{
"statusCode": 200,
"successful": true,
"message": null,
"data": {}
}
但是默认的AspNetCore的WebApi是没有特定的返回格式的,所以需要根据业务要求自己去开发定义。
原理的话直接去原文链接,我这里只讲用法
https://www.cnblogs.com/deali/p/16995384.html
用法
首先定义一个IApiResonse接口
public interface IApiResponse {
public int StatusCode { get; set; }
public bool Successful { get; set; }
public string? Message { get; set; }
}
public interface IApiResponse<T> : IApiResponse {
public T? Data { get; set; }
}
public interface IApiErrorResponse {
public Dictionary<string,object> ErrorData { get; set; }
}
接口定义完成后着手创建ApiResponse类实现该接口的方法。
public class ApiResponse<T> : IApiResponse<T> {
public ApiResponse() {
}
public ApiResponse(T? data) {
Data = data;
}
public int StatusCode { get; set; } = 200;
public bool Successful { get; set; } = true;
public string? Message { get; set; }
public T? Data { get; set; }
/// <summary>
/// 实现将 <see cref="ApiResponse"/> 隐式转换为 <see cref="ApiResponse{T}"/>
/// </summary>
/// <param name="apiResponse"><see cref="ApiResponse"/></param>
public static implicit operator ApiResponse<T>(ApiResponse apiResponse) {
return new ApiResponse<T> {
StatusCode = apiResponse.StatusCode,
Successful = apiResponse.Successful,
Message = apiResponse.Message
};
}
}
public class ApiResponse : IApiResponse, IApiErrorResponse {
public int StatusCode { get; set; } = 200;
public bool Successful { get; set; } = true;
public string? Message { get; set; }
public object? Data { get; set; }
/// <summary>
/// 可序列化的错误
/// <para>用于保存模型验证失败的错误信息</para>
/// </summary>
public Dictionary<string,object>? ErrorData { get; set; }
public ApiResponse() {
}
public ApiResponse(object data) {
Data = data;
}
public static ApiResponse NoContent(string message = "NoContent") {
return new ApiResponse {
StatusCode = StatusCodes.Status204NoContent,
Successful = true, Message = message
};
}
public static ApiResponse Ok(string message = "Ok") {
return new ApiResponse {
StatusCode = StatusCodes.Status200OK,
Successful = true, Message = message
};
}
public static ApiResponse Ok(object data, string message = "Ok") {
return new ApiResponse {
StatusCode = StatusCodes.Status200OK,
Successful = true, Message = message,
Data = data
};
}
public static ApiResponse Unauthorized(string message = "Unauthorized") {
return new ApiResponse {
StatusCode = StatusCodes.Status401Unauthorized,
Successful = false, Message = message
};
}
public static ApiResponse NotFound(string message = "NotFound") {
return new ApiResponse {
StatusCode = StatusCodes.Status404NotFound,
Successful = false, Message = message
};
}
public static ApiResponse BadRequest(string message = "BadRequest") {
return new ApiResponse {
StatusCode = StatusCodes.Status400BadRequest,
Successful = false, Message = message
};
}
public static ApiResponse BadRequest(ModelStateDictionary modelState, string message = "ModelState is not valid.") {
return new ApiResponse {
StatusCode = StatusCodes.Status400BadRequest,
Successful = false, Message = message,
ErrorData = new SerializableError(modelState)
};
}
public static ApiResponse Error(string message = "Error", Exception? exception = null) {
object? data = null;
if (exception != null) {
data = new {
exception.Message,
exception.Data
};
}
return new ApiResponse {
StatusCode = StatusCodes.Status500InternalServerError,
Successful = false,
Message = message,
Data = data
};
}
}
定义完成后就能使用了
列子:
public ApiResponse<Post> Get(string id) {
var post = _postService.GetById(id);
return post == null ? ApiResponse.NotFound() : new ApiResponse<Post>(post);
}
根据上面的代码,可以发现 ApiResponse.NotFound() 返回的是一个 ApiResponse 对象
但这接口的返回值明明是 ApiResponse<Post> 类型呀,这不是类型不一致吗?
不过在 ApiResponse<T> 中,我们定义了一个运算符重载,实现了 ApiResponse 类型到 ApiResponse<T> 的隐式转换,所以就完美解决这个问题,大大减少了代码量。
注意
除了这些以 ApiResponse 或 ApiResponse<T> 作为返回类型的接口,还有很多其他返回类型的接口,比如
public List<ConfigItem> GetAll() {
return _service.GetAll();
}
这些接口在 AspNetCore 生成响应的时候,会把这些返回值归类为 ObjectResult ,如果不做处理,就会直接序列化成不符合我们返回值规范的格式。
这个不行,必须对这部分接口的返回格式也统一起来。
因为种种原因,最终我选择使用过滤器来实现这个功能。
关于过滤器的详细用法,可以参考官方文档,本文就不展开了,直接上代码。
创建ResponseWrapperFilter.cs类
public class ResponseWrapperFilter : IAsyncResultFilter {
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) {
if (context.Result is ObjectResult objectResult) {
if (objectResult.Value is IApiResponse apiResponse) {
objectResult.StatusCode = apiResponse.StatusCode;
context.HttpContext.Response.StatusCode = apiResponse.StatusCode;
}
else {
var statusCode = objectResult.StatusCode ?? context.HttpContext.Response.StatusCode;
var wrapperResp = new ApiResponse<object> {
StatusCode = statusCode,
Successful = statusCode is >= 200 and < 400,
Data = objectResult.Value,
};
objectResult.Value = wrapperResp;
objectResult.DeclaredType = wrapperResp.GetType();
}
}
await next();
}
}
之后在 Program.cs 里注册一下这个过滤器。
var mvcBuilder = builder.Services.AddControllersWithViews(
options => { options.Filters.Add<ResponseWrapperFilter>(); }
);
完成
最后所有接口(可序列化的),返回格式就都变成了这样
{
"statusCode": 200,
"successful": true,
"message": null,
"data": {}
}
如果出现报错按Alt+Enter 快速下载nuget包就行了
如Microsoft.AspNetCore.Mvc.Core

浙公网安备 33010602011771号