在企业应用中,后端响应前端请求必须要带响应状态码,如状态码200表示请求正常响应,403代表权限认证失败,404代表资源不存在等。在Jersey框架中带响应状态码的响应一般都要通过Response对象实现,举例如下。
@Path("/book")
public class Book {
@Path("")
@GET
public Response get() throws Exception {
boolean exist = false;
if (!exist) {
//如果对象不存在
return Response.status(Response.Status.NOT_FOUND)
.entity("指定的书目不存在")
.build();
} else {
return Response.ok("书目信息")
.build();
}
}
}
通过查看代码,我们发现除了Response.ok()
方法比较简洁以外,其他响应状态码都要通过status
方法来指定,最后还要通过build方法构建,代码看起来罗里吧嗦。
那么,有没有更好的方式实现以上功能呢?能否通过抛出异常的方式实现非正常请求的统一处理?
Jersey的异常处理机制为上述问题提供了完美的解决之道。
Jersey框架的ExceptionMapper接口统一负责处理异常映射,在实现接口时实现toResponse
方法为异常处理指定响应方式。
/**
* 全局异常处理
*/
//@Provider是关键,如果不添加异常处理不会生效,只会在后端提示错误
@Provider
public class ApplicationExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception e) {
return Response
.status(Response.Status.BAD_REQUEST.getStatusCode())
.type(MediaType.APPLICATION_JSON)
.entity(new SimpleMsg(e.getMessage()))
.build();
}
}
注意:注解@Provider是关键,千万不能忘记,如果不添加异常处理不会生效,只会在后端提示错误。记得当初我尝试统一异常处理的时候,就因为这个注解没写,导致效果一直出不来,经过了一个多月才发现了这个问题。
以下模仿简单的获取图书信息的异常,在程序中强行抛出异常表示请求的书目不存在。
编写一个测试资源类,使其抛出异常。
@Path("/book")
public class Book {
@Path("")
@GET
public String get() throws Exception {
throw new Exception("指定的书目不存在");
}
}
在服务启动类中,注册exception包下的资源。
sh.setInitParameter("jersey.config.server.provider.packages", "org.bigdata.res,org.bigdata.exception");
服务启动类如下
public class JettyServer {
public static void main(String[] args) throws Exception {
int port = 8081;
Server server = new Server(port);
ServletHolder sh = new ServletHolder(ServletContainer.class);
sh.setInitParameter("jersey.config.server.provider.packages",
"org.bigdata.res,org.bigdata.exception");
ServletContextHandler apiContext = new ServletContextHandler(
ServletContextHandler.SESSIONS);
apiContext.addServlet(sh, "/*");
apiContext.setContextPath("/");
HandlerList handlerList = new HandlerList();
handlerList.addHandler(apiContext);
server.setHandler(handlerList);
server.start();
}
}
通过浏览器访问http://localhost:8081/book
,Jersey框架已经将报错信息友好地反馈给前端。
在关于Jersey框架的参数校验一文中,遗留了一个问题,@Size(min = 24, max = 24, message = “id必须为24位”)注解虽然提示了错误,但并没有提示message属性中定义的id必须为24位。
产生上述问题的原因是,Jersey框架抛出了异常,而我们并没有显示处理。解决这个问题的方法就是定义一个专门处理参数校验异常的ValidationExceptionMapper
。
@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
@Override
public Response toResponse(ValidationException e) {
StringBuilder strBuilder = new StringBuilder();
for (ConstraintViolation<?> cv : ((ConstraintViolationException) e).getConstraintViolations()) {
strBuilder.append(cv.getMessage() + ";");
}
return Response
.status(Response.Status.BAD_REQUEST.getStatusCode())
.type(MediaType.APPLICATION_JSON)
.entity(new SimpleMsg(strBuilder.toString()))
.build();
}
}
案例Github地址:https://github.com/majxbear/jetty-rest