传统常规方式统一接口返回值 以及Abp Vnext 统一接口返回值

文章摘要:本文介绍了两种统一API接口返回值的实现方式。传统方式通过定义ResponseModel和ResponseModel<T>基类,包含状态码、消息和数据字段,并提供了扩展方法SetSuccess和SetFail来设置响应结果。ABP框架方式则更全面,通过WrapResult<T>类、WrapResultAttribute特性及ResultExceptionFilter过滤器实现,能自动处理异常并统一返回格式。后者还包含日志记录、状态码转换等功能,通过在控制器或Action上添加WrapResult特性即可启用统一响应格式。两种方式都实现了成功/失败状态的标准化返回,提高了API接口的一致性。

传统常规方式统一接口返回值

Domain.Shared

// <summary>
    /// 响应结果
    /// </summary>
    public class ResponseModel
    {
        //响应状态码
        public ResponseCode code { get; set; }
        /// <summary>
        /// 响应消息
        /// </summary>
        public string message { get; set; }
    }
    /// <summary>
    /// 有对象的响应结果
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class ResponseModel<T> : ResponseModel
    {
        public T data { get; set; }
    }

    public enum ResponseCode
    {
        Success = 0,
        Fail = 1
    }
    public static class ResponseExtend
    {
        public static ResponseModel SetSuccess(this ResponseModel response, string message = "")
        {
            ResponseModel result = new ResponseModel();
            result.code = ResponseCode.Success;
            result.message = message == string.Empty ? "操作成功" : message;
            return result;
        }
        public static ResponseModel<T> SetSuccess<T>(this ResponseModel<T> response, T data = default, string message = "")
        {
            response.code = ResponseCode.Success;
            response.message = message == string.Empty ? "操作成功" : message;
            response.data = data;
            return response;
        }
        public static ResponseModel SetFail(this ResponseModel response, string message = "")
        {
            response.code = ResponseCode.Fail;
            response.message = message == string.Empty ? "操作失败" : message;
            return response;
        }
        public static ResponseModel<T> SetFail<T>(this ResponseModel<T> response, string message = "", T data = default)
        {
            response.code = ResponseCode.Fail;
            response.message = message == string.Empty ? "操作成功" : message;
            response.data = data;
            return response;
        }

Application

public async Task<ResponseModel<PagedResultDto<PageSPWxTagOutput>>> SPPageAsync(PageSPWxTagInput input)
{

    ResponseModel<PagedResultDto<PageSPWxTagOutput>> response = System.Text.Json.JsonSerializer.Deserialize<ResponseModel<PagedResultDto<PageSPWxTagOutput>>>(
     System.Text.Json.JsonSerializer.Serialize(new { code = 0, msg = "更新成功",date=new PagedResultDto<PageSPWxTagOutput>() }));


try
{
 
}
catch (Exception ex)
{
    response.SetFail("获取SDK数据失败:" + ex.Message);
    return response;
} 

}



    /// <summary>
    /// 创建盛派微信标签
    /// </summary>
    public async Task<ResponseModel> CreateAsync(CreateSPWxTagInput input)
    {


        ResponseModel response = System.Text.Json.JsonSerializer.Deserialize<ResponseModel>(
            System.Text.Json.JsonSerializer.Serialize(new { code = 0, msg = "创建成功" }));
        //todo 选中的公众号 
        if (Guid.TryParse(input.idaccessToken, out Guid guid))
        {

        }
        else
        {
            response.SetFail("转换失败:字符串不是有效的 GUID 格式");
            return response; 
        } 
        var aAuthorizer_Access_Token = await _SpCommonAppService.getAuthorizer_Access_Token(guid);
        if (string.IsNullOrWhiteSpace(aAuthorizer_Access_Token))
        {
            response.SetFail("获取aAuthorizer_Access_Token失败");
            return response;
        }
        try {
           
        }
        catch (Exception ex)
        {
            response.SetFail("获取SDK数据失败:" + ex.Message);
            return response;
        }  
/
        return response; 
    }

httpAPI

 [HttpPost("Create")]
 [SwaggerOperation(summary: "创建盛派微信标签", Tags = new[] { "SPWxTags" })]
 public async Task<ResponseModel> CreateAsync(CreateSPWxTagInput input)
 {
     return await _sPWxTagAppService.CreateAsync(input);
 }
 [HttpPost("SPPage")]
 [SwaggerOperation(summary: "SP分页查询盛派微信标签", Tags = new[] { "SPWxTags" })]
 public async Task<ResponseModel<PagedResultDto<PageSPWxTagOutput>>> SPPageAsync(PageSPWxTagInput input)
 {
     return await _sPWxTagAppService.SPPageAsync(input);
 }

---

ABP Vnext Vue 的实现

{
    // 返回格式类似这种
    "success": false,
    "message": "请求失败",
    "data": null,
    "code": 500
}
  • 定义返回类型
public class WrapResult<T>
{
    private bool Success { get; set; }
    private string Message { get; set; }
    private T Data { get; set; }
    private int Code { get; 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;
    }
}

实现思路

  • 定义 WrapResultAttribute
public class WrapResultAttribute : Attribute
{
}
  • 实现 IAsyncExceptionFilter(拦截异常,抛异常时指定返回格式)
public class ResultExceptionFilter : IFilterMetadata, IAsyncExceptionFilter, ITransientDependency
{
    private ILogger<ResultExceptionFilter> Logger { get; set; }
    private readonly IExceptionToErrorInfoConverter _errorInfoConverter;
    private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder;
    private readonly IJsonSerializer _jsonSerializer;
    private readonly AbpExceptionHandlingOptions _exceptionHandlingOptions;
    public ResultExceptionFilter(
        IExceptionToErrorInfoConverter errorInfoConverter,
        IHttpExceptionStatusCodeFinder statusCodeFinder,
        IJsonSerializer jsonSerializer,
        IOptions<AbpExceptionHandlingOptions> exceptionHandlingOptions)
    {
        _errorInfoConverter = errorInfoConverter;
        _statusCodeFinder = statusCodeFinder;
        _jsonSerializer = jsonSerializer;
        _exceptionHandlingOptions = exceptionHandlingOptions.Value;
        Logger = NullLogger<ResultExceptionFilter>.Instance;
    }
    public async Task OnExceptionAsync(ExceptionContext context)
    {
        if (!ShouldHandleException(context))
        {
            return;
        }
        await HandleAndWrapException(context);
    }
    protected virtual bool ShouldHandleException(ExceptionContext context)
    {
        if (context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.GetCustomAttributes(typeof(WrapResultAttribute), true).Any())
        {
            return true;
        }
        if (context.ActionDescriptor.GetMethodInfo().GetCustomAttributes(typeof(WrapResultAttribute), true).Any())
        {
            return true;
        }

        return false;
    }
    protected virtual async Task HandleAndWrapException(ExceptionContext context)
    {
        // 处理异常信息
        context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true");
        var statusCode = (int)_statusCodeFinder.GetStatusCode(context.HttpContext, context.Exception);
        context.HttpContext.Response.StatusCode = 200;
        var remoteServiceErrorInfo = _errorInfoConverter.Convert(context.Exception, _exceptionHandlingOptions.SendExceptionsDetailsToClients);
        remoteServiceErrorInfo.Code = context.HttpContext.TraceIdentifier;
        remoteServiceErrorInfo.Message = SimplifyMessage(context.Exception);
        // 返回格式统一
        var result = new WrapResult<object>();
        result.SetFail(remoteServiceErrorInfo.Message);
        // HttpResponse
        context.Result = new ObjectResult(result);
        // 写日志
        var logLevel = context.Exception.GetLogLevel();
        var remoteServiceErrorInfoBuilder = new StringBuilder();
        remoteServiceErrorInfoBuilder.AppendLine($"---------- {nameof(RemoteServiceErrorInfo)} ----------");
        remoteServiceErrorInfoBuilder.AppendLine(_jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true));
        Logger.LogWithLevel(logLevel, remoteServiceErrorInfoBuilder.ToString());
        Logger.LogException(context.Exception, logLevel);
        await context.HttpContext
            .RequestServices
            .GetRequiredService<IExceptionNotifier>()
            .NotifyAsync(
                new ExceptionNotificationContext(context.Exception)
            );
        context.Exception = null; //Handled!
    }
    private string SimplifyMessage(Exception error)
    {
        string message = string.Empty;
        switch (error)
        {
            case AbpAuthorizationException e:
                return message = "Authenticate failure!";
            case AbpValidationException e:
                return message = "Request param validate failure!";
            case EntityNotFoundException e:
                return message = "not found the entity!";
            case BusinessException e:
                return message = $"{e.Message}";
            case NotImplementedException e:
                return message = "not implement!";
            default:
                return message = "server internal error!";
        }
    }
}

注册 Filter

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

使用

  • 在 Controller 上或者 Action 上打上 WrapResultAttribute 特性
  • 例如
    [Route("Permissions")]
    [WrapResult]
    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);
        }

    }

 Abp vNext 基本使用 - .NET好耶 - 博客园

ABP.Next系列01-有啥?像啥?infrastructure -Fstyle-优快云博客

ABP.Next系列02 搭个API框架要多久 项目下载 运行 -Fstyle_【abp vnext】下载并运行web api项目详细教程文档-优快云博客

 Abp Vnext Pro Vben5-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云草桑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值