Websphere Liberty上的JAX-RS异常处理

时间:2018-05-21 10:27:27

标签: java jax-rs websphere-liberty open-liberty

我需要一些帮助来理解Websphere Liberty(18.0.0.1)如何处理JAX-RS端点调用中抛出的异常。我正在使用Liberty功能jaxrs-2.0,因此应该由WLP提供实现。

现在,我的应用程序有一个POST HTTP端点接受JSON有效负载,我想为所有可能的错误客户端输入提供自定义错误消息。

这是一个以我预期的方式工作的案例:

  1. 客户端发送application/xml而不是application/json
  2. 容器抛出ClientErrorException
  3. 我可以使用自己的异常映射器(实现ExceptionMapper<WebApplicationException>来处理此异常(实际上是为了处理所有Web应用程序异常,我很好)
  4. 这样我就可以格式化错误信息,用ID标记错误,无论需要什么。那很好
  5. 以下情况不适合我:

    1. 客户端发送application/json,但空身
    2. 在这种情况下的核心例外是java.io.EOFException: No content to map to Object due to end of input - 是的,看起来很准确
    3. 现在我无法弄清楚 - 而不是将此EOFException包装到某种WebApplicationException(我可以轻松处理),WLP将异常问题包装到JaxRsRuntimeException
    4. 这里有几点:

      • 我不想创建一个实现ExceptionMapper<JaxRsRuntimeException>的映射器,因为该异常不是JAX-RS 2.0规范的一部分,我必须提供导入到JaxRsRuntimeException并将应用程序与一些Liberty连接起来特定图书馆。
      • 一种可能的解决方案是让我的映射器实现泛型ExceptionMapper<RuntimeException>和字符串检查它是否找到classname'JaxRsRuntimeException'的异常然后处理它。但这对我来说似乎并不合适。

      那么,WLP设计是不是在这种情况下不给我一个WebApplicationException?处理这种情况的优雅解决方案是什么?

      由于

      编辑:添加了部分源代码。

      REST端点和资源方法:

      @Path("/books")
      public class BookEndpoint {
      
          @POST
          @Consumes(MediaType.APPLICATION_JSON)
          public Response createBook(Book book, @Context UriInfo uriInfo) {
              bookDao.create(book);
              UriBuilder builder = uriInfo.getAbsolutePathBuilder();
              builder.path(Integer.toString(book.getId()));
              return Response.created(builder.build()).entity(book).build();
          }
      }
      

      使用JAXB注释的实体:

      @XmlRootElement
      public class Book {
      
          private int id;
          private String title;
      
          // getters, setters
      }
      

      异常堆栈跟踪:

      com.ibm.ws.jaxrs20.JaxRsRuntimeException: java.io.EOFException: No content to map to Object duto end of input
          at org.apache.cxf.jaxrs.utils.JAXRSUtils.toJaxRsRuntimeException(JAXRSUtils.java:1928)
          at [internal classes]
          at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
          at com.ibm.ws.webcontainer.filter.FilterInstanceWrapper.doFilter(FilterInstanceWrapper.java:201)
          at [internal classes]
          at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
          at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
          at java.lang.Thread.run(Thread.java:745)
      Caused by: java.io.EOFException: No content to map to Object duto end of input
          at org.codehaus.jackson.map.ObjectMapper._initForReading(ObjectMapper.java:2775)
          at [internal classes]
          at java.security.AccessController.doPrivileged(Native Method)
          at org.apache.cxf.jaxrs.utils.JAXRSUtils.readFromMessageBodyReader(JAXRSUtils.java:1413)
          at [internal classes]
          ... 48 more
      

2 个答案:

答案 0 :(得分:2)

不是直接回答“为什么WLP包装异常..etc”但是可能像你一样在"ExceptionMapper<Exception>"上添加异常拦截器并且反复地在“原因”上迭代检查java.io.EOFException是否是其中之一...

答案 1 :(得分:2)

这是基于JAX-RS 2.0 Spec的第3.3.4节(和4.5.1节)的预期行为。这些部分描述了如何处理来自JAX-RS资源和提供程序的异常 - 简而言之:

  1. 如果例外是WebApplicationException,则会自动映射到Response
  2. 如果注册的ExceptionMapper可以处理抛出的异常,那么将用于生成响应。
  3. 未经检查的异常会传播到容器(即Liberty的JAX-RS实现代码)。
  4. 未映射的异常必须通过特定于容器的异常处理,然后适当地传播到底层容器 - 在这种情况下,必须将ServletException传递给Web容器。
  5. JaxRsRuntimeException用于满足第4步。

    在这种情况下,内置的JSON提供程序(基于Jackson 1.X)正在抛出EOFException。由于EOFException(或其任何超类)没有异常映射器,因此它最终通过ServletException映射到JaxRsRuntimeException

    为了让应用程序处理这种情况,有几种不同的选择:

    1. 您可以注册一个特定于此例外类型的ExceptionMapperEOFException或其中任何一个超类 - 即IOException)。您不需要为JaxRsRuntimeException注册映射器,因为该异常仅在Liberty内部使用 - 并且不应映射。如果您看到传递给ExceptionMapper的JaxRsRuntimeException,那么您应该打开IBM的支持案例,因为这可能是一个错误。
    2. 使用ExceptionMapper<EOFException>,只要从提供者或资源中抛出EOFException,您就可以返回特定的响应。

      1. 您可以注册自己的MessageBodyReader,它将JSON转换为对象(使用Jackson或任何其他JSON序列化代码),但它将以您希望的方式处理空消息体 - 例如,将其转换为{{ 1}}或使用某种默认对象实例。由于用户注册的提供商优先于内置提供商,因此将使用此MBR代替Liberty的基于Jackson的MBR。
      2. 这种方法绝对可以让您更好地控制数据的反序列化以及异常处理。

        1. 注册一个null提供程序,该提供程序将在邮件正文为空时中止。这是一个例子:

          ContainerRequestFilter
        2. 我使用WebSphere Liberty May 2018 Beta成功测试了选项1和3。我没有亲自测试过这种情况的选项2,但基于过去使用自定义MBR,这应该可行。

          要记住的一点是,当Liberty GAs @Provider public class EmptyBodyCheckFilter implements ContainerRequestFilter { @Override public void filter(ContainerRequestContext crc) throws IOException { if (crc.getEntityStream().available() < 1) { crc.abortWith(Response.status(400).entity("Invalid request - empty message body").build()); } } } 功能时,它将使用JSONB作为内置提供程序来序列化/反序列化JSON而不是Jackson。我使用JAX-RS 2.1(也在May Beta中)测试了您的场景,而不是jaxrs-2.1,JSONB代码抛出EOFException。如果您认为可以转移到JAX-RS 2.1,那么我建议选项2或3.选项1要求您为JAX-RS 2.1创建新的NoSuchElementException

          希望这有帮助,

          安迪