之前写过使用自定义返回类的方式来统一接口数据返回格式,.Net Core webapi RestFul 统一接口数据返回格式-CSDN博客
但是这存在一个问题,不是所有接口会按照定义的数据格式返回,除非每个接口都返回我们自定义的类,这种实现起来不太现实。
类似这样,定义一个接口:
返回的只是只有user的json对象:
这显然不是我们想要的结果,我们想要的结果是这样:
{
"statusCode": 200,
"successful": true,
"message": null,
"data": {
"userId": "001",
"userName": "小王",
"password": "123"
}
}
我们需要不管接口定义的返回类型是什么,最后的结果都是统一的数据格式,需要实现这个功能就需要自定义一个过滤器来实现。
具体实现代码如下:
自定义一个过滤器类?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();
}
}
在代码中进行判断,当响应的类型是?ObjectResult
?时,把这个响应结果拿出来,再判断是不是?IApiResponse
?类型。
前面我们介绍过,所有?ApiResponse
?都实现了?IApiResponse
?这个接口,所以可以判断是不是?IApiResponse
?类型来确定这个返回结果是否包装过。
没包装的话就给包装一下,就这么简单。
附上?ApiResponse.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; }
}
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
};
}
}
之后在?Program.cs
?里注册一下这个过滤器
services.AddControllers(options =>
{
options.Filters.Add<ResponseWrapperFilter>();
});
再次调用GetUser接口,可以看到已经包装成统一的数据格式返回了:
而对于之前已经定义返回类型是ApiResponse的接口也不会重复包装: