SpringBoot全局Controller返回值格式统一处理
SpringBoot全局Controller返回值格式统一处理
一、Controller返回值格式统一
1、WebResult类
在 Controller对外提供服务的时候,我们都需要统一返回值格式。一般定义一个 WebResult类。
统一返回值(WebResult类)格式如下:
{
"success": true,
"code": 200000,
"message": null,
"data": {
"pageList": [
"张三",
"ccc"
],
"paginator": {
"currentPage": 1,
"pageSize": 2,
"total": 3,
"pages": 4
}
}
}
WebResult类信息:
@Data
@ApiModel(value = "Web结果集")
public class WebResult<T> implements Serializable {
private static final long serialVersionUID = -4350499690382193884L;
/**
* 是否成功, 默认false
*/
@ApiModelProperty(value = "是否成功, 默认false")
private Boolean success = false;
/**
* 返回状态码
*/
@ApiModelProperty(value = "返回状态码")
private Integer code;
/**
* 返回信息
*/
@ApiModelProperty(value = "返回信息")
private String message;
/**
* 返回数据
*/
@ApiModelProperty(value = "返回数据")
private T data;
public static <T> WebResult<T> ok() {
WebResult<T> webResult = new WebResult<>();
webResult.setSuccess(true);
webResult.setCode(WebHttpCode.SERVICE_SUCCESS);
webResult.setMessage("操作成功");
return webResult;
}
public static <T> WebResult<T> ok(T data) {
WebResult<T> webResult = new WebResult<>();
webResult.setSuccess(true);
webResult.setCode(WebHttpCode.SERVICE_SUCCESS);
webResult.setMessage("操作成功");
webResult.setData(data);
return webResult;
}
public static <T> WebResult<T> error() {
WebResult<T> webResult = new WebResult<>();
webResult.setSuccess(false);
webResult.setCode(WebHttpCode.SERVICE_ERROR);
webResult.setMessage("操作失败");
return webResult;
}
public WebResult message(String message) {
this.setMessage(message);
return this;
}
public WebResult data(T data) {
this.setData(data);
return this;
}
}
如果我们不做全局 Controller统一处理返回时,就出需要业务在每个 Controller类中返回 WebResult类。同时在全局异常中也返回 WebResult类。
有没有一种跟优雅的方式处理呢?当然有
- 业务不需要对所有 Controller都使用一个返回值(WebResult类),Controller可以需要返回原始值或者自定义的基类,然后处理器统一对返回值(WebResult类)进行封装并输出。
- 同时也可以添加自定义注解,此注解用于忽略返回值封装,按照 Controller原始值返回。
下面我们使用 HandlerMethodReturnValueHandler接口来实现。
2、相关类说明
2.1 HandlerMethodReturnValueHandler接口
使用不同策略处理从调用处理程序方法的返回值,策略处理顶层接口,自定义返回值格式需要实现此接口。
org.springframework.web.method.support.HandlerMethodReturnValueHandler
- supportsReturnType:设置支持返回值类型
- handleReturnValue:处理返回值基础参数
2.2 RequestMappingHandlerAdapter类
请求映射处理适配,包含了参数、返回值处理器等信息
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
- HandlerMethodReturnValueHandlerComposite内部维护了HandlerMethodReturnValueHandler列表
3.3 RequestResponseBodyMethodProcessor类
属于HandlerMethodReturnValueHandler子类。
org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor
- 主要功能是对请求和响应体的做处理的方法,所有属于RequestResponseBodyMethodProcessor的子类都需要替换为自定义返回值处理
全局Controller返回值统一处理实现原理就是:在bean初始化的时候,获取到所有处理器数组,然后将所有是 RequestResponseBodyMethodProcessor处理器子类对返回值处理的过程替换为自定义返回值处理器处理。
这样当调用对应返回值处理器时,将会使用到自定义的返回值处理器,也就是所有返回值都会按照规定的进行处理。
同时,我们也可以自定义注解(作用于Controller类或者方法级别忽略返回值封装),然后在自定义返回值处理器中忽略返回值封装。
二、全局Controller返回值统一处理实战
1、自定义注解
自定义注解,作用于Controller类或者方法级别忽略返回值封装。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface IgnoreResponseBodyWrap {
}
2、创建自定义返回值处理器类
创建自定义处理器类实现 HandlerMethodReturnValueHandler接口,主要用于实现自定义返回值内容,不需要注入容器。
下面代码中的 BaseXXXResult是业务的基类。你可以自定义或者使用你们约定的基类。
/**
* Controller 返回值格式统一处理
*/
public class ResponseBodyWrapHandler implements HandlerMethodReturnValueHandler {
private final HandlerMethodReturnValueHandler delegate;
public ResponseBodyWrapHandler(HandlerMethodReturnValueHandler delegate) {
this.delegate = delegate;
}
/**
* 设置支持返回值类型
*
* @param returnType
* @return
*/
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return delegate.supportsReturnType(returnType);
}
/**
* 处理返回值基础参数
*
* @param returnValue 方法的返回值对象
* @param returnType 封装方法参数说明的辅助类(方法的返回值类型)
* @param mavContainer 用于设置模型和视图的容器
* @param webRequest 当前的请求对象
* @throws Exception
*/
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 如果类或者方法含有不包装注解则忽略包装
IgnoreResponseBodyWrap ignoreResponseBodyWrap = returnType.getDeclaringClass().getAnnotation(IgnoreResponseBodyWrap.class);
if (ignoreResponseBodyWrap != null) {
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
ignoreResponseBodyWrap = returnType.getMethodAnnotation(IgnoreResponseBodyWrap.class);
if (ignoreResponseBodyWrap != null) {
delegate.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
return;
}
// 返回统一格式
Object obj = null;
if (returnValue instanceof WebResult) {
obj = returnValue;
} else if (returnValue instanceof BasePageResult) {
BasePageResult basePageResult = (BasePageResult) returnValue;
ErrorCode errorCode = basePageResult.getErrorCode();
Map<String, Object> pageData = new HashMap<>();
pageData.put(CommonConstants.PAGE_LIST, basePageResult.getPageList());
pageData.put(CommonConstants.PAGINATOR, basePageResult.getPaginator());
obj = WebResult.ok().data(pageData).message(errorCode == null ? null : errorCode.getMessage());
} else if (returnValue instanceof BaseOperateResult) {
BaseOperateResult baseOperateResult = (BaseOperateResult) returnValue;
ErrorCode errorCode = baseOperateResult.getErrorCode();
boolean success = baseOperateResult.isSuccess();
if (success) {
obj = WebResult.ok().data(baseOperateResult.getId()).message(errorCode == null ? null : errorCode.getMessage());
} else {
obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
}
} else if (returnValue instanceof BaseDataResult) {
BaseDataResult baseDataResult = (BaseDataResult) returnValue;
ErrorCode errorCode = baseDataResult.getErrorCode();
boolean success = baseDataResult.isSuccess();
if (success) {
obj = WebResult.ok().data(baseDataResult.getData()).message(errorCode == null ? null : errorCode.getMessage());
} else {
obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
}
} else if (returnValue instanceof BaseResult) {
BaseResult baseResult = (BaseResult) returnValue;
ErrorCode errorCode = baseResult.getErrorCode();
boolean success = baseResult.isSuccess();
if (success) {
obj = WebResult.ok().message(errorCode == null ? null : errorCode.getMessage());
} else {
obj = WebResult.error().message(errorCode == null ? null : errorCode.getMessage());
}
} else {
obj = WebResult.ok(returnValue);
}
delegate.handleReturnValue(obj, returnType, mavContainer, webRequest);
}
}
3、注册自定义返回值处理器类
将所有 RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器。
@Component
public class ResponseBodyWrapFactoryBean implements InitializingBean {
private final RequestMappingHandlerAdapter adapter;
@Autowired
public ResponseBodyWrapFactoryBean(RequestMappingHandlerAdapter adapter) {
this.adapter = adapter;
}
@Override
public void afterPropertiesSet() throws Exception {
List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers();
if (returnValueHandlers.size() > 0) {
// 将内置的返回值处理器进行替换
List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(returnValueHandlers);
decorateHandlers(handlers);
adapter.setReturnValueHandlers(handlers);
}
}
/**
* 将所有RequestResponseBodyMethodProcessor返回值处理器替换为自定义的返回值处理器
*
* @author tianxincode@163.com
* @since 2020/10/12
*/
private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) {
for (HandlerMethodReturnValueHandler handler : handlers) {
if (handler instanceof RequestResponseBodyMethodProcessor) {
// 替换为自定义返回值处理器
ResponseBodyWrapHandler decorator = new ResponseBodyWrapHandler(handler);
int index = handlers.indexOf(handler);
handlers.set(index, decorator);
break;
}
}
}
}
4、全局异常处理类
一般项目都会有一个全局异常处理类
@Slf4j
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = Exception.class)
private WebResult handlerException(Exception e) {
log.error("Exception 异常 -> error:", e);
WebResult webResult = new WebResult();
webResult.setSuccess(false);
webResult.setCode(WebHttpCode.SYSTEM_ERROR);
webResult.setMessage("系统异常,请联系管理员");
return webResult;
}
@ExceptionHandler(value = RuntimeException.class)
private WebResult handlerRuntimeException(RuntimeException e) {
log.error("RuntimeException 异常 -> error:", e);
WebResult webResult = new WebResult();
webResult.setSuccess(false);
webResult.setCode(WebHttpCode.SYSTEM_ERROR);
webResult.setMessage("系统异常");
return webResult;
}
@ExceptionHandler(value = ServiceException.class)
private WebResult handlerServiceException(ServiceException e) {
log.error("ServiceException 异常 -> error:", e);
WebResult webResult = new WebResult();
webResult.setSuccess(false);
webResult.setCode(WebHttpCode.SERVICE_ERROR);
webResult.setMessage(e.getMessage());
return webResult;
}
}
5、Controller示例
@RestController
@RequestMapping("/project2")
@Slf4j
@Api(value = "Project2Controller", tags = {"Project2相关操作接口"})
public class Project2Controller {
@GetMapping("/successPage")
@ApiOperation(value = "successPage接口")
public BaseResult successPage() {
log.info("successPage接口");
List list = new ArrayList();
list.add("张三");
list.add("ccc");
BasePageResult basePageResult = new BasePageResult<>();
basePageResult.setSuccess(Boolean.TRUE);
basePageResult.setPageList(list);
basePageResult.setPaginator(new Paginator(1, 2, 3, 4));
return basePageResult;
}
@GetMapping("/get")
@ApiOperation(value = "get接口")
@IgnoreResponseBodyWrap
public List get() {
log.info("get接口");
ProjectDO projectDO = new ProjectDO();
projectDO.setId(10L);
projectDO.setName("项目10");
projectDO.setDepartmentId(0L);
projectDO.setDescr("");
projectDO.setDelFlag(false);
projectDO.setCreateTime(new Date());
projectDO.setUpdateTime(new Date());
List list = new ArrayList();
list.add("张三");
list.add("ccc");
return list;
}
@GetMapping("/get2")
@ApiOperation(value = "get2接口")
public BaseDataResult get2() {
log.info("get2接口");
ProjectDO projectDO = new ProjectDO();
projectDO.setId(10L);
projectDO.setName("项目10");
projectDO.setDepartmentId(0L);
projectDO.setDescr("");
projectDO.setDelFlag(false);
projectDO.setCreateTime(new Date());
projectDO.setUpdateTime(new Date());
BaseDataResult baseDataResult = new BaseDataResult();
baseDataResult.setSuccess(Boolean.TRUE);
baseDataResult.setData(projectDO);
return baseDataResult;
}
@GetMapping("/get3")
@ApiOperation(value = "get3接口")
public List get3() {
log.info("get3接口");
ProjectDO projectDO = new ProjectDO();
projectDO.setId(10L);
projectDO.setName("项目10");
projectDO.setDepartmentId(0L);
projectDO.setDescr("");
projectDO.setDelFlag(false);
projectDO.setCreateTime(new Date());
projectDO.setUpdateTime(new Date());
List list = new ArrayList();
list.add("张三");
list.add("ccc");
return list;
}
/**
* 文件下载接口
*/
@GetMapping("/download")
@ApiOperation(value = "文件下载接口")
public void download(HttpServletResponse response) throws SerialException {
try {
// 获取要下载的文件
File file = new File("D:\\TempFiles\\xxxxxx.xlsx");
String fileName = file.getName();
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + new String(fileName.getBytes("UTF-8"), "iso-8859-1"));
IOUtils.copy(new FileInputStream(file), response.getOutputStream());
} catch (Exception e) {
log.info("文件下载异常 -> e=", e);
throw new ServiceException("文件下载异常");
}
}
@GetMapping("/systemError")
@ApiOperation(value = "systemError接口")
public BaseResult systemError() {
log.info("systemError接口");
int i = 2 / 0;
BaseResult baseResult = new BaseResult();
baseResult.setSuccess(Boolean.TRUE);
return baseResult;
}
}
– 求知若饥,虚心若愚。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)