Graceful Response基于graceful-response v3.0 定制调整,提供一个Spring Boot体系下的优雅响应处理器,提供一站式统一返回值封装、全局异常处理、自定义异常错误码等功能,使用Graceful Response进行web接口开发不仅可以节省大量的时间,还可以提高代码质量,使代码逻辑更清晰。
定制调整内容主要包括:
本项目使用案例见:https://gitee.com/shawn_lxc/graceful-response-example
通常我们进行Java Web API接口时,大部分的Controller代码是这样的:
public class Controller {
@GetMapping("/query")
@ResponseBody
public Response query(Parameter params) {
Response res = new Response();
try {
//1.校验params参数,非空校验、长度校验
if (illegal(params)) {
res.setCode(1);
res.setMsg("error");
return res;
}
//2.调用Service的一系列操作
Data data = service.query(params);
//3.将操作结果设置到res对象中
res.setData(data);
res.setCode(0);
res.setMsg("ok");
return res;
} catch (BizException1 e) {
//4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
res.setCode(1024);
res.setMsg("error");
return res;
} catch (BizException2 e) {
//4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
res.setCode(2048);
res.setMsg("error");
return res;
} catch (Exception e) {
//4.异常处理:一堆丑陋的try...catch,如果有错误码的,还需要手工填充错误码
res.setCode(1);
res.setMsg("error");
return res;
}
}
}
这段代码存在什么问题呢?
Data data=service.query(params);
其他代码不管是正常执行还是异常处理,都是为了异常封装、把结果封装为特定的格式,例如以下格式:
{
"code": 0,
"msg": "ok",
"data": {
"id": 1,
"name": "username"
}
}
这样的逻辑每个接口都需要处理一遍,都是繁琐的重复劳动。
现在,在引入Graceful Response组件后,我们只要直接返回业务结果,Graceful Response即可自动完成response的响应状态码处理以及响应报文的规范化处理。
graceful-response已发布至maven中央仓库,可以直接引入到项目中,maven依赖如下:
<dependency>
<groupId>com.gitee.shawn_lxc</groupId>
<artifactId>graceful-response</artifactId>
<version>1.0.0</version>
</dependency>
目前最新版本为1.0.0
。
@EnableGracefulResponse
@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
import org.springframework.web.bind.annotation.ResponseStatus;
@Controller
public class Controller {
@RequestMapping("/get")
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public UserInfoView get(Long id) {
log.info("id={}", id);
return UserInfoView.builder().id(id).name("name" + id).build();
}
}
这个接口直接返回了 UserInfoView
的实例对象,调用接口时,Graceful Response将自动返回以下格式报文:
{
"id": 1,
"name": "name1"
}
每个请求API添加正常响应状态码,如上接口正常返回时,响应码为200。
接口正常状态时,响应码定义,建议采用如下规范:
200 OK [GET]: 服务器端成功返回用户请求的数据;批量重启、批量删除操作成功
201 CREATED [POST/PUT/PATCH]: 用户新建或修改数据成功。
202 Accepted 表示一个请求已经进入后台排队(一般是异步任务)。
204 NO CONTENT -[DELETE]: 用户删除数据成功、修改数据成功。
public class Controller {
@RequestMapping("/void")
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public void testVoidResponse() {
//业务操作
}
}
testVoidResponse
方法的返回时void,调用这个接口时,将返回:
{
"status": {
"code": "200",
"msg": "success"
},
"payload": {}
}
在引入Graceful Response后,Service将:
public interface ExampleService {
UserInfoView query1(Query query);
}
public class ExampleServiceImpl implements ExampleService {
@Resource
private UserInfoMapper mapper;
UserInfoView query1(Query query) {
UserInfo userInfo = mapper.findOne(query.getId());
if (Objects.isNull(userInfo)) {
//这里直接抛自定义异常
throw new NotFoundException();
}
//……后续业务操作
}
}
/**
* NotFoundException的定义,使用@ExceptionMapper注解修饰
* code:代表接口的异常码
* msg:代表接口的异常提示
*/
@ExceptionMapper(httpStatus = HttpStatus.NOT_FOUND, code = "1404", msg = "找不到对象")
public class NotFoundException extends RuntimeException {
}
当Service方法抛出NotFoundException异常时,接口将响应状态码设置404(HttpStatus.NOT_FOUND),并将错误码和错误信息作为响应体返回,不需要手工set。
{
"error_code": "1404",
"error_msg": "找不到对象"
}
验证:启动example工程后,请求http://localhost:8811/example/notfound
@ExceptionMapper
设计的初衷,是将状态码、异常与错误码关联起来,用户只需要抛异常,不需要再关注异常与状态码、错误码的对应关系。
在不自定义新异常类的情况下,也能可以按照预期返回状态码、错误码和异常信息,利用GracefulResponseException
异常类,用户只需要抛出该异常即可。
public class Service {
public void method() {
throw new GracefulResponseException(HttpStatus.XXX, "自定义的错误码","自定义的错误信息");
}
}
为简化使用,提供了GracefulResponse
通用工具类,在需要抛出GracefulResponseException
时,只需要调用raiseException
方法即可。 这样做的目的是将用户的关注点从异常转移到错误码。
示例如下:
public class Service {
public void method() {
//当condition==true时,抛出GracefulResponseException异常,返回自定义的错误码和错误信息
if (condition) {
GracefulResponse.raiseException(HttpStatus.NOT_FOUND, "自定义的错误码", "自定义的错误信息");
}
}
}
引入Graceful Response后,使用@ValidationStatusCode
注解,可以非常方便地支持validation校验异常。
@ValidationStatusCode
注解目前只有一个code
属性,用于指定参数校验异常时的错误码,错误提示则取自validation校验框架。参数校验异常时,状态码均为默认值是400(HttpStatus.BAD_REQUEST)。
@Data
public class UserInfoQuery {
@NotNull(message = "userName is null !")
@Length(min = 6, max = 12)
@ValidationStatusCode(code = "520")
private String userName;
}
当userName
字段任意一项校验不通过时,接口将会返回异常码520
和校验注解中的message
,并将状态码设置为400:
{
"status": {
"code": "520",
"msg": "userName is null !"
},
"payload": {}
}
详细见example工程ExampleController的validateDto方法
http://localhost:8811/example/validateDto
注意:@ValidationStatusCode校验参数对象字段的情况,code取值顺序为:会先取字段上的注解,再去该属性所在对象的类(即UserInfoQuery类)上的注解,再取全局配置的参数异常码
gr.defaultValidateErrorCode
,最后取默认的全局默认的错误码(默认code=1)
直接在Controller方法中进行参数校验:
public class ExampleController {
@RequestMapping("/validateMethodParam")
@ResponseBody
@ValidationStatusCode(code = "1314")
public void validateMethodParam(@NotNull(message = "userId不能为空") Long userId,
@NotNull(message = "userName不能为空") Long userName) {
//省略业务逻辑
}
}
当userId、或者userName校验不通过时,将会返回code=1314,msg为对应的校验信息。
{
"error_code": "1314",
"error_msg": "userId不能为空"
}
详细见example工程ExampleController的validateMethodParam方法
http://localhost:8811/example/validateMethodParam
注意:@ValidationStatusCode校验Controller方法参数字段的情况,code取值顺序为:会先取当前方法上的注解,再去该方法所在类(即ExampleController类)上的注解,再取全局配置的参数异常码
gr.defaultValidateErrorCode
,最后取默认的全局默认的错误码(默认code=1)
以下是使用Graceful Response进行异常、错误码处理的开发步骤。
创建自定义异常,采用 @ExceptionMapper
注解修饰,注解的 code
属性为返回码,msg
属性为错误提示信息
@ExceptionMapper(httStatus = HttpStatus.INTERNAL_SERVER_ERROR, code = "1007", msg = "有内鬼,终止交易")
public static final class RatException extends RuntimeException {
}
Service执行具体逻辑,需要抛异常的时候直接抛出去即可,不需要在关心异常与错误码关联的问题
public class Service {
public void illegalTransaction() {
//需要抛异常的时候直接抛
if (hasRat()) {
logger.error("有内鬼终止交易");
throw new RatException();
}
doIllegalTransaction();
}
}
Controller调用Service
public class Controller {
@RequestMapping("/test3")
public void test3() {
logger.info("test3: RuntimeException");
//Controller中不会进行异常处理,也不会手工set错误码,只关心核心操作,其他的统统交给Graceful Response
exampleService.illegalTransaction();
}
}
在浏览器中请求controller的/test3方法,有异常时将会返回:
{
"error_code": "1007",
"error_msg": "有内鬼,终止交易"
}
案例工程( https://gitee.com/shawn_lxc/graceful-response-example.git )启动后, 通过浏览器访问一个不存在的接口,例如 http://localhost:8811/example/get2?id=1
如果没开启Graceful Response,将会跳转到404页面页面,主要原因是应用内部产生了 NoHandlerFoundException
异常。如果开启了Graceful
Response,默认会返回code=1的错误码,并且状态码为404。
这类非自定义的异常,如果需要自定义一个错误码返回,将不得不对每个异常编写Advice逻辑,在Advice中设置状态码、错误码和提示信息,这样做非常繁琐。
Graceful Response可以非常轻松地解决给这类外部异常定义错误码和提示信息的问题。
以下为操作步骤:
@ExceptionAliasFor
注解修饰
import org.springframework.http.HttpStatus;
@ExceptionAliasFor(httpStatus = HttpStatus.NOT_FOUND, code= "1404", msg = "not found", aliasFor = NoHandlerFoundException.class)
public class NotFoundException extends RuntimeException {
}
code:捕获异常时返回的错误码
msg:为提示信息
aliasFor:表示将成为哪个异常的别名,通过这个属性关联到对应异常。
创建一个继承了AbstractExceptionAliasRegisterConfig的配置类,在实现的registerAlias方法中进行注册。
@Configuration
public class GracefulResponseConfig extends AbstractExceptionAliasRegisterConfig {
@Override
protected void registerAlias(ExceptionAliasRegister aliasRegister) {
aliasRegister.doRegisterExceptionAlias(NotFoundException.class);
}
}
再次访问 http://localhost:8811/example/get2?id=1 ,服务端将返回以下json,正是在ExceptionAliasFor中定义的内容
{
"error_code": "1404",
"error_msg": "not found"
}
gr.printExceptionInGlobalAdvice
是否打印异常日志,默认为true
gr.defaultErrorCode
自定义的失败错误码,默认为1
gr.defaultErrorMsg
自定义的失败提示,默认为error
gr.defaultValidateErrorCode
全局的参数校验错误码,默认等于gr.defaultErrorCode
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。