美文网首页SpringJava技术升华javaWeb学习
Spring Boot Controller 统一返回格式

Spring Boot Controller 统一返回格式

作者: 破地瓜 | 来源:发表于2019-05-31 17:29 被阅读168次

    功能开发中,我们会有这种需求,希望所有的数据返回统一的格式,包含状态码,数据,错误信息,当前时间,比如:

    {"code":200,"data":null,"message":"OK","timestamp":1559292578716}
    

    首先我们需要有这么一个类

    @Data
    public class Result<T> {
        private long timestamp;
        private String message;
        private int code=SUCCESS;
        private T data;
        @Data
        public static class Builder<T> {
            //部分代码略
        }
    }
    

    但是我们不能在每一个Controller上的返回格式都定义为Result

    Spring Boot 如何在不改变原有的Controller的基础上实现呢?
    我们可以借助Spring提供的ResponseBodyAdvice实现

    public interface ResponseBodyAdvice<T> {
        boolean supports(MethodParameter var1, Class<? extends HttpMessageConverter<?>> var2);
    
        @Nullable
        T beforeBodyWrite(@Nullable T var1, MethodParameter var2, MediaType var3, Class<? extends HttpMessageConverter<?>> var4, ServerHttpRequest var5, ServerHttpResponse var6);
    }
    

    该接口只有两个方法supportsbeforeBodyWrite

    • supports 方法返回boolean,判断是否支持Controller的返回类型
    • beforeBodyWrite 写出之前的处理,此文中主要依靠此方法,T var1就是Controller返回的对象

    写一个我们自己的实现

    @ControllerAdvice()
    public class ResponseAdvisor implements ResponseBodyAdvice<Object> {
        @Override
        public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
            return true;
        }
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                      ServerHttpResponse response) {
            return new Result.Builder<>().data(body).build();
        }
    }
    

    系统中原有的Controller

    @RestController
    public class DemoController {
        @GetMapping("/echo/{name}")
        public String echo(@PathVariable("name") String name){
            return "hello:"+name;
        }
        @GetMapping("/user/{id}")
        public User getUserById(@PathVariable("id") Long id){
            User user=new User(id,"Lucy",18);
            return user;
        }
        @GetMapping("/download")
        public ResponseEntity<FileSystemResource> download() {
            File file = new File("d:/1.jpg");
            HttpHeaders headers = new HttpHeaders();
            headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
            try {
                headers.add("Content-Disposition", "attachment; filename=" + new String(file.getName().getBytes("GB2312"),"ISO-8859-1"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            headers.add("Pragma", "no-cache");
            headers.add("Expires", "0");
            return ResponseEntity
                    .ok()
                    .headers(headers)
                    .contentLength(file.length())
                    .contentType(MediaType.APPLICATION_OCTET_STREAM)
                    .body(new FileSystemResource(file));
        }
    }
    

    分别进行测试

    {
        "timestamp": 1559293805111, 
        "message": "OK", 
        "code": 200, 
        "data": {
            "id": 1, 
            "name": "Lucy", 
            "age": 18
        }
    }
    
    java.lang.ClassCastException: com.podigua.demo.core.Result cannot be cast to java.lang.String
    
    java.lang.ClassCastException: com.podigua.demo.core.Result cannot be cast to org.springframework.core.io.Resource
    

    三个测试.发现有两个出错, 出错的原因是找不到对应的HttpMessageConverter,HttpMessageConverter 是一组消息转换器,以下列出部分MessageConverter

    • StringHttpMessageConverter 负责字符串类型的数据的读取或者写出
    • ByteArrayHttpMessageConverter 负责二进制格式数据的读取或者写出
    • ResourceHttpMessageConverter 负责资源文件的读取或者写出
    • FormHttpMessageConverter 负责读取form提交的数据application/x-www-form-urlencoded
    • MappingJacksonHttpMessageConverter 负责json格式数据的读取或者写出

    原因就是缺少 MappingJackson2HttpMessageConverterResourceHttpMessageConverter

    我们调整下代码,增加这两个消息转换器

    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer  {
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(0, new MappingJackson2HttpMessageConverter());
            converters.add(1, new ResourceHttpMessageConverter());
        }
    }
    

    资源类数据,不能按照这种格式返回,最终返回的是文件流,我们调整下代码ResponseAdvisorbeforeBodyWrite方法,若返回值已经是Result类型也不需要处理

    @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                      Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                      ServerHttpResponse response) {
            if (body instanceof Resource) {
                return body;
            }
            if (body instanceof Result) {
                return body;
            }
            return new Result.Builder<>().data(body).build();
        }
    

    Coltroller抛出了异常呢? 需要AOP捕获异常,并将异常信息与状态码封装为Result的数据即可

    项目github地址

    相关文章

      网友评论

        本文标题:Spring Boot Controller 统一返回格式

        本文链接:https://www.haomeiwen.com/subject/aasgtctx.html