SpringJava技术升华javaWeb学习

Spring Boot Controller 统一返回格式

2019-05-31  本文已影响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

写一个我们自己的实现

@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

原因就是缺少 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地址

上一篇下一篇

猜你喜欢

热点阅读