在RestFul风格盛行的年代,大部分接口都需要一套统一的数据返回格式,那么我们怎么才能保证使用统一的json数据格式返回呢,下面给大家简单介绍一下:
假如我们需要接口统一返回一下数据格式:
{"statusCode": 200,"successful": true,"message": null,"data": {"name": "张三","age": 0,"address": null,"sex": null,"phoneNum": null,"email": null}
}
json对象中data是返回的实际结果对象,可以是一个对象、一个list、一个字符串、一个数字......
但是整体的json格式要以上面的为准
一般这种情况我们有两种实现方式:
1、自定义一个统一返回类
2、使用过滤器(Filter)实现
下面先介绍第一种,自定义一个接口返回类:
创建对应的接口和实现类
- ApiResponse.cs//实现类
- IApiResponse.cs://几个相关的接口
IApiResponse.cs
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; }}
保证了所有相关对象都来自 IApiResponse
接口
ApiResponse.cs
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};}}
分别实现类带有泛型的 ApiResponse<T> 类和 ApiResponse 类
注意在泛型类中,使用运算符重载,实现了 ApiResponse
到 ApiResponse<T>
的隐式转换。
在接口实现处返回一个 ApiResponse<T> 对象:
[HttpGet]public ApiResponse<UserEntity> Get(string name){var list = new List<UserEntity>() {new UserEntity() { Name = "张三" },new UserEntity(){Name = "李四"}};var user = list.Find(p => p.Name == name);return user == null ? ApiResponse.NotFound() : new ApiResponse<UserEntity>(user);}
注意看最后一句
return user == null ? ApiResponse.NotFound() : new ApiResponse<UserEntity>(user);
ApiResponse.NotFound()
返回的是一个 ApiResponse
对象
但这接口的返回值明明是 ApiResponse<
UserEntity>
类型呀,这不是类型不一致吗?
不过在 ApiResponse<T>
中,我们定义了一个运算符重载,实现了 ApiResponse
类型到 ApiResponse<T>
的隐式转换,所以就完美解决这个问题,大大减少了代码量。
来看一下最后运行效果: