美文网首页
spring-boot 实现自定义返回数据类型

spring-boot 实现自定义返回数据类型

作者: 破地瓜 | 来源:发表于2018-12-03 17:00 被阅读8次

进行功能开发时,想将Controller的返回值封装成以下格式

{timestamp:1543823794412,code:200,message:"OK",data:result}
其中result为各种类型的值(json,string,object,array)等

待返回数据类型,部分代码如下

public class Result<T> {
    public static final int SUCCESS = 200;
    public static final int ERROR = 500; 
    //响应时间
    private long timestamp;
    //提示信息
    private String message;
    //状态码
    private int code=SUCCESS;
    //返回数据
    private T data; 
}

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();
        user.setId(id);
        user.setName("Lucy");
        user.setAge(18);
        return user;
    }
}

postman 测试结果如下;这里直接贴结果

url result
http://localhost:8080/user/1 {"id": 1, "name": "Lucy","age": 18}
http://localhost:8080/echo/kitty hello:kitty

若想实现自定义返回数据类型需要借助ResponseBodyAdvice接口

代码如下

@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();
    }

}

使用postman再次测试,结果如下

{
    "timestamp": 1543825204365,
    "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

此异常与MessageConverter有关。controller层中返回的类型是String,但是在ResponseBodyAdvice实现类中,我们把响应的类型修改成了Result。这就导致了,上面的这段代码在选择处理MessageConverter的时候,依旧根据之前的String类型选择对应String类型的StringMessageConverter。而在StringMessageConverter类型,他只接受String类型的返回类型,我们在ResponseBodyAdvice中将返回值从String类型改成Result类型之后,调用StringMessageConverter方法发生类型强转。Result无法转换成String,所以会发生类型转换异常。

既然是MessageConverter有问题,新增一个可以处理的MessageConverter即可,代码如下

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

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/*");
    }
    
}
{
    "timestamp": 1543826321331,
    "message": "OK",
    "code": 200,
    "data": "hello:kitty"
}

备注:上一篇文章中将AOP时,捕获了Controller,并封装为Result格式,
若抛出异常时,则会有两个Result对象类似于

{
    "timestamp": 1543826321331,
    "message": "错误信息",
    "code": 500,
    "data": {
        "timestamp": 1543826321331,
        "message": "错误信息",
        "code": 500,
        "data": null
    }
}

这显然不是我们要的结果;所以ResponseAdvisor 需要调整代码

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

字符串的问题搞定了,那么Controller提供下载功能的时候呢?

下载功能

  @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));
    }

其实也是一样的处理方式,再加一个转换器,再调整一下ResponseAdvisor的代码

代码详见 https://github.com/jiabiaoli/demo/tree/master/unified

相关文章

网友评论

      本文标题:spring-boot 实现自定义返回数据类型

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