开发中经常会遇到需要统一返回错误码或者错误信息,虽然可以用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
类有两个属性,name
与age
。
启动项目后访问http://localhost:8080/user
可以得到如下返回值
{
"name": "kotlin",
"age": 12
}
普通的实现方式
直接修改这个getUser
方法
-
新定义一个类
data class Result(val errorCode:Int, val errorMessage:String, val data:Any?)
-
修改
getUser
接口的返回值@RequestMapping("user") @ResponseBody fun getUser(): Result { return Result(0,"",User("kotlin", 12)) }
-
重启服务并且访问
http://localhost:8080/user
可以得到如下返回值:{ "errorCode": 0, "errorMessage": "", "data": { "name": "kotlin", "age": 12 } }
可以发现这种是实现了文章开头的返回格式的。
但是这种实现方式有一个很严重的问题是,每增加一个接口都需要加上一些格式化代码。这样会造成大量的冗余代码的出现。而且如何以后重构代码时希望去掉这种格式的返回值的话,每一个接口都需要删除掉代码。
使用ResponseBodyAdvice的实现方式
spring4.1中引入了ResponseBodyAdvice
接口,继承这个接口可以很容易的实现格式化返回值的需求。
-
新增一个
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) } }
-
重启服务并且访问
http://localhost:8080/user
可以得到如下返回值:{ "errorCode": 0, "errorMessage": "", "data": { "name": "kotlin", "age": 12 } }
可以发现返回值也实现了文章开头需要的格式。
这种方式的优势在于,Controller层无耦合,可以进行方便的重构。
总结
当需要对返回值进行统一的处理时,可以使用ResponseBodyAdvice
来统一实现。免去了每一个Controller每一个接口都进行修改的问题。
思考
使用ResponseBodyAdvice
虽然可以实现正常流程的返回值格式化。但是并没有实现党业务抛出异常时以及正常的业务出错时errorCode
与errorMessage
需要返回相应的错误代码。该如何实现呢?