一、接口返回类型封装原因
对接口返回的数据格式进行封装,使开发只关注业务,减少数据格式转换
1.异常结果:可通过自定义异常抛出异常码和异常原因
2.数据结果:接口在返回结果时,自动封装
二、通过扩展MVC的ActionFilterAttribute方法来现实封装
1、扩展类实现
public class ApiResultFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
if (context.Exception != null)
{
// 如果有异常,处理异常并封装响应
if (context.Exception is BusExpection expection)
{
context.Result = new ObjectResult(ApiResponse.NoContent(expection.ErrorCode(), expection.ErrorMsg()));
}
else
{
context.Result = new ObjectResult(ApiResponse.NoContent(ApiErrorCode.发生异常, context.Exception.Message));
}
context.ExceptionHandled = true; // 标记异常已处理
}
else if (context.Result is ObjectResult objectResult)
{
// 如果没有异常,封装正常的响应
context.Result = new ObjectResult(new ApiResponse() { Code = CommonErrorMsg.成功.GetValue(), Message = CommonErrorMsg.成功.GetName(), Result = objectResult.Value });
}
}
public override void OnActionExecuting(ActionExecutingContext context)
{
// 在动作执行前可以添加逻辑,但在这个场景下我们不需要
}
}
2、接口固定返回类定义
public class ApiResponse : ApiResponse<object>
{
public static ApiResponse OK(object result) => OK(result, ApiErrorCode.成功);
public static ApiResponse OK(object result, ApiErrorCode code) =>
new() { Result = result, Code = (int)code, Message = code.ToString() };
public static ApiResponse NoContent() => NoContent(ApiErrorCode.成功);
public static ApiResponse NoContent(ApiErrorCode code) => NoContent(code, code.ToString());
public static ApiResponse NoContent(ApiErrorCode code, string message) => new ApiResponse
{
Code = (int)code,
Message = message
};
public static ApiResponse NoContent(int code, string message) => new ApiResponse
{
Code = code,
Message = message
};
public static ApiResponse Error(Exception error) => new ApiResponse
{
Code = (int)ApiErrorCode.发生异常,
Message = error.Message
};
public static ApiResponse Error(string errorMessage) => NoContent(ApiErrorCode.发生异常, errorMessage);
}
public class ApiResponse<T>
{
/// <summary>
/// 响应code(0则成功,否则失败)
/// response code(0 is success, otherwise is failure)
/// </summary>
[Required]
public int Code { get; set; } = (int)ApiErrorCode.成功;
/// <summary>
/// 响应消息
/// response message
/// </summary>
public string Message { get; set; } = "";
/// <summary>
/// 响应结果
/// response result
/// </summary>
public T Result { get; set; }
public ApiResponse<T> OK(T result) => OK(result, ApiErrorCode.成功);
public ApiResponse<T> OK(T result, ApiErrorCode code)
{
this.Code = (int)code;
this.Result = result;
return this;
}
public ApiResponse<T> NoContent() => NoContent(ApiErrorCode.成功);
public ApiResponse<T> NoContent(ApiErrorCode code) => NoContent(code, code.ToString());
public ApiResponse<T> NoContent(ApiErrorCode code, string message)
{
this.Code = (int)code;
this.Message = message;
return this;
}
public ApiResponse<T> Error(Exception error)
{
this.Code = (int)ApiErrorCode.发生异常;
this.Message = error.Message;
return this;
}
}
3、自定义异常信息
public class BusExpection : Exception
{
private readonly string errMsg;
private readonly int Code;
public BusExpection (string msg = "",
int code = (int)ApiErrorCode.发生异常) : base(msg)
{
errMsg = msg;
Code = code;
}
public string ErrorMsg()
{
return errMsg ?? base.Message;
}
public int ErrorCode()
{
return Code;
}
}
4、 定义接口异常枚举
public enum ApiErrorCode
{
成功 = 0,
失败 = 10001,
授权失败 = 10002,
发生异常 = 10003,
参数验证错误 = 10004,
重复操作 = 10005
}
5、接口实现
// 在program.cs里配置全局过滤器
builder.Services.AddControllers(config => { config.Filters.Add(typeof(ApiResultFilterAttribute)); })
.AddNewtonsoftJson();
只需要在方法上添加[ApiResultFilter]特性。也可以给接口方法所在类添加该特性,扩展会应用该类下所有方法。
[ApiResultFilter]
public Task<Student> Get(int id)
{
//抛出自定义异常错误,可指定错误码和错误信息
throw new BusExpection("参数异常", 100003);
return "123";
}
[ApiResultFilter]
public Task<Student> Get(int id)
{
//原生异常错误信息,无法指定错误code
throw new Exception("出错了!");
return "123";
}
[ApiResultFilter]
public Task<Student> Get(int id)
{
return new Student() { Name = "姓名1", Money = 123 };
}
6、接口实现注意事项
- 业务逻辑处理时直接抛出自定义异常,不需要再进行异常处理。
- 业务处理后的数据集直接通过return 返回,不需要额外封装。