在常见的bs前后分离开发中,我们一般会统一返回的格式,这样更方便前端进行处理。
创建统一响应类
我们先创建一个统一返回的类,为了方便我们会提供两个静态方法(成功和失败)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| public class UniformResponse<T> { public string? StatusCode { get; set; } public bool Succeeded { get; set; } public T? Data { get; set; } public string? Error { get; set; } public long Timestamp { get; set; } = DateTime.Now.Ticks; public dynamic? Extra { get; set; }
public static UniformResponse<T> Succeed(T? data, string statusCode = "200") => new() { StatusCode = statusCode, Succeeded = true, Data = data};
public static UniformResponse<T> Failure(string errors, string statusCode = "500") => new() { Error = errors, StatusCode = statusCode }; }
|
实现控制器方法结果包装
对控制器方法结果包装我们需要实现IResultFilter或者IAsyncResultFilter,这两个筛选器可以很方便的操作控制器方法返回的结果,我们通过对不同结果的不同处理,然后实现结果包装。这里主要针对WebAPI返回的一个情况,如果需要判断其他IActionResult的派生类则需另加。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| public class UniformResponseFilter : IAsyncResultFilter { private readonly UniformResponseExtraHelper _extraHelper; private readonly ILogger<UniformResponseFilter> _logger;
public UniformResponseFilter(UniformResponseExtraHelper extraHelper, ILogger<UniformResponseFilter> logger) { _extraHelper = extraHelper; _logger = logger; }
public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { if (context.Result is FileResult) { await next(); return; } UniformResponse<object> targetResult; if (context.Result is BadRequestObjectResult badResult) { string error = "异常"; if (badResult.Value is ValidationProblemDetails details) { error = details.Title + ";" + string.Join(";", details.Errors.Select(e => $"【{e.Key}:{string.Join(",", e.Value)}】")); } _logger.LogError(error); targetResult = UniformResponse<object>.Failure(error); } else if (context.Result is EmptyResult) { targetResult = UniformResponse<object>.Succeed(null); } else if (context.Result is JsonResult jr) { targetResult = UniformResponse<object>.Succeed(jr.Value); } else { var objResult = (ObjectResult)context.Result; if (objResult.StatusCode == 200 || objResult.StatusCode is null) { var value = objResult.Value; object? result = context.Result == null ? true : value; targetResult = UniformResponse<object>.Succeed(result); } else { if (objResult.Value is ProblemDetails details) { targetResult = UniformResponse<object>.Failure(details.Title!); } else { targetResult = UniformResponse<object>.Failure("请求异常"); } } }
targetResult.Extra = _extraHelper.GetExtraDatas();
context.Result = new ObjectResult(targetResult); await next(); } }
|
支持额外信息的返回
我们在UniformResponse中有一个Extra的属性,这个就是方便后面我们在不修改原本Data属性的格式下额外返回的一个东西,它将支持在整个请求中随时返回任意格式的数据,同时我们会提供一个添加额外信息的帮助类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class UniformResponseExtraHelper { private readonly Dictionary<string, object> extraDatas; public UniformResponseExtraHelper() { extraDatas = new(); }
public void Add(string name, object data) => extraDatas.TryAdd(name, data);
public Dictionary<string, object>? GetExtraDatas() => extraDatas.Any() ? extraDatas : null; }
|
在Program.cs添加
1 2 3 4 5 6
| builder.Services.AddScoped<UniformResponseExtraHelper>(); builder.Services.AddControllers(options => { options.Filters.Add<UniformResponseFilter>(); });
|
运行测试
新建测试控制器1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| [ApiController] [Route("api/[controller]/[action]")] public class TestController : ControllerBase { private readonly UniformResponseExtraHelper _extraHelper; private readonly ILogger<TestController> _logger;
public TestController(UniformResponseExtraHelper extraHelper, ILogger<TestController> logger) { _extraHelper = extraHelper; _logger = logger; }
public async Task<string> UniformResponseTest() { _extraHelper.Add("info","额外信息"); return await Task.FromResult("统一响应体测试"); } }
|
请求结果:
1 2 3 4 5 6 7 8 9 10
| { "statusCode": "200", "succeeded": true, "data": "统一响应体测试", "error": null, "timestamp": 638140879982390000, "extra": { "info": "额外信息" } }
|