Jersey框架:统一异常处理

为什么要统一处理异常?

在企业应用中,后端响应前端请求必须要带响应状态码,如状态码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的异常处理机制

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框架:统一异常处理_第1张图片

统一处理表单校验异常

在关于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();
    }
}

做出以上修改后,校验请求返回的响应如下。
Jersey框架:统一异常处理_第2张图片

案例Github地址:https://github.com/majxbear/jetty-rest

你可能感兴趣的:(Jersey,Jersey系列:实践全干货)