kotlin使用ResponseAdvice返回统一风格的json数据

开发中经常会遇到需要统一返回错误码或者错误信息,虽然可以用restful形式编程,复用http status code状态码来当作错误码,但是仍然会有很多的限制。因此很多情况下我们会定义成一种统一的json格式来处理返回值。比如:

{"errorCode":0,"errorMessage":"",data:{"name":"kotlin",age:12}}

例子

有一个Controller如下所示

@Controller
class MyController {

    @RequestMapping("user")
    @ResponseBody
    fun getUser(): User {
        return User("kotlin", 12)
    }
}

data class User(val name: String, val age: Int)

Controller下有一个getUser的接口,返回一个User类,User类有两个属性,nameage
启动项目后访问http://localhost:8080/user可以得到如下返回值

{
  "name": "kotlin",
  "age": 12
}

普通的实现方式

直接修改这个getUser方法

  1. 新定义一个类

    data class Result(val errorCode:Int, val errorMessage:String, val data:Any?)
    
  2. 修改getUser接口的返回值

    @RequestMapping("user")
    @ResponseBody
    fun getUser(): Result {
    return Result(0,"",User("kotlin", 12))
    }
  3. 重启服务并且访问http://localhost:8080/user可以得到如下返回值:

    {
      "errorCode": 0,
    "errorMessage": "",
    "data": {
    "name": "kotlin",
    "age": 12
    }
    }

    可以发现这种是实现了文章开头的返回格式的。

但是这种实现方式有一个很严重的问题是,每增加一个接口都需要加上一些格式化代码。这样会造成大量的冗余代码的出现。而且如何以后重构代码时希望去掉这种格式的返回值的话,每一个接口都需要删除掉代码。

使用ResponseBodyAdvice的实现方式

spring4.1中引入了ResponseBodyAdvice接口,继承这个接口可以很容易的实现格式化返回值的需求。

  1. 新增一个MyAdvice继承ResponseBodyAdvice

    @ControllerAdvice
    class MyAdvice : ResponseBodyAdvice<Any> {
    //此方法可以过滤需要进行格式化输出的请求,本例中默认全部支持。因此直接返回true
    override fun supports(returnType: MethodParameter, converterType: Class<out HttpMessageConverter<*>>): Boolean {
    return true
    }
    //此方法将Controller中的返回值body,包装成格式化输出的格式,因此直接返回了Result的实例
    override fun beforeBodyWrite(body: Any?,
    returnType: MethodParameter,
    selectedContentType: MediaType,
    selectedConverterType: Class<out HttpMessageConverter<*>>,
    request: ServerHttpRequest,
    response: ServerHttpResponse): Any? {
    return Result(0, "", body)
    }
    }
  2. 重启服务并且访问http://localhost:8080/user可以得到如下返回值:

    {
      "errorCode": 0,
    "errorMessage": "",
    "data": {
    "name": "kotlin",
    "age": 12
    }
    }

    可以发现返回值也实现了文章开头需要的格式。
    这种方式的优势在于,Controller层无耦合,可以进行方便的重构。

总结

当需要对返回值进行统一的处理时,可以使用ResponseBodyAdvice来统一实现。免去了每一个Controller每一个接口都进行修改的问题。

思考

使用ResponseBodyAdvice虽然可以实现正常流程的返回值格式化。但是并没有实现党业务抛出异常时以及正常的业务出错时errorCodeerrorMessage需要返回相应的错误代码。该如何实现呢?