@Datapublic final class CommonResult { private int status = 1; private String errorCode = ""; private String errorMsg = ""; private T resultBody; public CommonResult() { } public CommonResult(T resultBody) { this.resultBody = resultBody; }}
配置
没错,我们需要借助几个关键注解来完成一下相关配置:
@EnableWebMvc@Configurationpublic class UnifiedReturnConfig { @RestControllerAdvice("com.example.unifiedreturn.api") static class CommonResultResponseAdvice implements ResponseBodyAdvice
到这里就结束了,我们就可以纵情的写任何 RESTful API 了,所有的返回值都会有统一的 JSON 结构
@RestController@RequestMapping("/users")public class UserController { @GetMapping("") public List getUserList(){ List userVoList = Lists.newArrayListWithCapacity(2); userVoList.add(UserVo.builder().id(1L).name("日拱一兵").age(18).build()); userVoList.add(UserVo.builder().id(2L).name("tan").age(19).build()); return userVoList; }}
打开浏览器输入地址测试: http://localhost:8080/users/ ,我们可以看到返回了 List JSON 数据
继续添加 RESTful API,根据用户 ID 查询用户信息
@GetMapping("/{id}")public UserVo getUserByName(@PathVariable Long id){ return UserVo.builder().id(1L).name("日拱一兵").age(18).build();}
打开浏览器输入地址测试: http://localhost:8080/users/1 ,我们可以看到返回了单个 User JSON 数据
添加一个返回值类型为 ResponseEntity 的 API
@GetMapping("/testResponseEntity")public ResponseEntity getUserByAge(){ return new ResponseEntity(UserVo.builder().id(1L).name("日拱一兵").age(18).build(), HttpStatus.OK);}
打开浏览器输入地址测试: http://localhost:8080/users/testResponseEntity ,我们可以看到同样返回了单个 User JSON 数据
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware, InitializingBean { ...}
该类实现了 InitializingBean 接口,我在 Spring Bean 生命周期之"我从哪里来"? 这篇文章中明确说明了 Spring Bean 初始化的几个关键,其中 InitializingBean 接口的 afterPropertiesSet 方法就是关键之一,在 RequestMappingHandlerAdapter 类中同样重写了该方法:
@Overridepublic void afterPropertiesSet() { // Do this first, it may add ResponseBody advice beans initControllerAdviceCache(); if (this.argumentResolvers == null) { List resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.initBinderArgumentResolvers == null) { List resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); }}
其实还没完,你有没有想过,如果我们的 API 方法返回值是 org.springframework.http.ResponseEntity 类型,我们可以指定 HTTP 返回状态码,但是这个返回值会直接放到我们的 beforeBodyWrite 方法的 body 参数中吗?如果这样做很明显是错误的,因为 ResponseEntity 包含很多我们非业务数据在里面,那 Spring 是怎么帮我们处理的呢?
@Overridepublic void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { ... if (responseEntity instanceof ResponseEntity) { int returnStatus = ((ResponseEntity>) responseEntity).getStatusCodeValue(); outputMessage.getServletResponse().setStatus(returnStatus); if (returnStatus == 200) { if (SAFE_METHODS.contains(inputMessage.getMethod()) && isResourceNotModified(inputMessage, outputMessage)) { // Ensure headers are flushed, no body should be written. outputMessage.flush(); // Skip call to converters, as they may update the body. return; } } } // Try even with null body. ResponseBodyAdvice could get involved. writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage); // Ensure headers are flushed even if no body was written. outputMessage.flush();}