Skip to content

统一返回值格式

  • 在使用 abp 的过程中,如果提供给第三方接口要实现返回值统一需要怎么做?
{
    // 返回格式类似这种
    "success": false,
    "message": "请求失败",
    "data": null,
    "code": 500
}
  • 定义返回类型
public class WrapResult<T>
{
        public bool Success { get; private set; }

        public string Message { get; private set; }

        public T Data { get; private set; }

        public int Code { get; private set; }

        public WrapResult()
        {
            Success = true;
            Message = "Success";
            Data = default;
            Code = 200;
        }

        public void SetSuccess(T data, string message = "Success", int code = 200)
        {
            Success = true;
            Data = data;
            Code = code;
        }

        public void SetFail(string message = "Fail", int code = 500)
        {
            Success = false;
            Message = message;
            Code = code;
        }
}

实现思路

  • 定义 DontWrapResultAttribute
public class DontWrapResultAttribute : Attribute
{
}
  • 实现 IAsyncExceptionFilter(拦截异常,抛异常时指定返回格式)
public sealed class ResultExceptionFilter : IAsyncExceptionFilter, ITransientDependency
    {
        public async Task OnExceptionAsync(ExceptionContext context)
        {
            if (!ShouldHandleException(context))
            {
                return;
            }

            await HandleAndWrapException(context);
        }

        private bool ShouldHandleException(ExceptionContext context)
        {
            if (context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.GetCustomAttributes(typeof(DontWrapResultAttribute), true).Any())
            {
                return true;
            }

            if (context.ActionDescriptor.GetMethodInfo().GetCustomAttributes(typeof(DontWrapResultAttribute), true).Any())
            {
                return true;
            }

            return false;
        }

        private async Task HandleAndWrapException(ExceptionContext context)
        {
            var exceptionHandlingOptions = context.GetRequiredService<IOptions<AbpExceptionHandlingOptions>>().Value;
            var exceptionToErrorInfoConverter = context.GetRequiredService<IExceptionToErrorInfoConverter>();
            var remoteServiceErrorInfo = exceptionToErrorInfoConverter.Convert(context.Exception, options =>
            {
                options.SendExceptionsDetailsToClients = exceptionHandlingOptions.SendExceptionsDetailsToClients;
                options.SendStackTraceToClients = exceptionHandlingOptions.SendStackTraceToClients;
            });

            var logLevel = context.Exception.GetLogLevel();

            var remoteServiceErrorInfoBuilder = new StringBuilder();
            remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------");
            remoteServiceErrorInfoBuilder.AppendLine(context.GetRequiredService<IJsonSerializer>().Serialize(remoteServiceErrorInfo, indented: true));

            var logger = context.GetService<ILogger<ResultExceptionFilter>>(NullLogger<ResultExceptionFilter>.Instance);

            logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString());

            logger.LogException(context.Exception, logLevel);

            await context.GetRequiredService<IExceptionNotifier>().NotifyAsync(new ExceptionNotificationContext(context.Exception));
            context.HttpContext.Response.StatusCode = 200;
            var result = SimplifyMessage(context);
            context.Result = new ObjectResult(result);
            context.Exception = null; //Handled!
        }

        private WrapResult<object> SimplifyMessage(ExceptionContext context)
        {
            var result = new WrapResult<object>();
            var localizer = context.GetService<IStringLocalizer<AbpProResource>>();
            switch (context.Exception)
            {
                case AbpAuthorizationException:
                    result.SetFail("权限不足", 401);
                    break;
                case AbpValidationException:
                    result.SetFail("请求参数验证失败", 400);
                    break;
                case EntityNotFoundException:
                    result.SetFail("实体不存在", 506);
                    break;
                case NotImplementedException:
                    result.SetFail("未实现功能", 507);
                    break;
                default:
                {
                    if (context.Exception is IHasErrorCode codeException)
                    {
                        result.SetFail(localizer[codeException.Code]);
                        foreach (var key in context.Exception.Data.Keys)
                        {
                            result.SetFail(result.Message.Replace("{" + key + "}", context.Exception.Data[key]?.ToString()));
                        }
                    }
                    else
                    {
                        result.SetFail(context.Exception.Message);
                    }

                    break;
                }
            }

            return result;
        }
    }

注册 Filter

 public override void ConfigureServices(ServiceConfigurationContext context)
 {
    context.Services.AddMvc(options =>
        {
            options.Filters.Add(typeof(ResultExceptionFilter));
        });
 }

使用

  • 在 Controller 上或者 Action 上打上 DontWrapResultAttribute 特性
  • 例如
    [Route("Permissions")]
    [DontWrapResult]
    public class PermissionController : AbpProController,IRolePermissionAppService
    {
        private readonly IRolePermissionAppService _rolePermissionAppService;

        public PermissionController(IRolePermissionAppService rolePermissionAppService)
        {
            _rolePermissionAppService = rolePermissionAppService;
        }

        [HttpPost("tree")]
        [SwaggerOperation(summary: "获取角色权限", Tags = new[] { "Permissions" })]
        [WrapResult] //控制器上打了 action上就不需要
        public Task<PermissionOutput> GetPermissionAsync(GetPermissionInput input)
        {
            return _rolePermissionAppService.GetPermissionAsync(input);
        }

    }