jjzjj

java - Websphere Liberty 上的 JAX-RS 异常处理

coder 2024-04-01 原文

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

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

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

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

这种情况不适合我:

  1. 客户端发送application/json , 但 body 是空的
  2. 本例中的核心异常(exception)是java.io.EOFException: No content to map to Object due to end of input - 是的,看起来很准确
  3. 现在我想不通的是 - 而不是包装这个 EOFException进入某种WebApplicationException (我可以轻松处理),WLP 将异常问题包装到 JaxRsRuntimeException

这里有几点:

  • 我不想创建一个实现 ExceptionMapper<JaxRsRuntimeException> 的映射器因为该异常不是 JAX-RS 2.0 规范的一部分,我必须提供对 JaxRsRuntimeException 的导入并将应用程序与一些特定于 Liberty 的库连接起来。
  • 一个可能的解决方案是让我的映射器实现一个通用的 ExceptionMapper<RuntimeException>和字符串检查是否发现类名“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

最佳答案

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

  1. 如果异常是 WebApplicationException , 然后它会自动映射到一个 Response .
  2. 如果有 ExceptionMapper注册可以处理抛出的异常,然后将用于生成响应。
  3. 未经检查的异常会传播到容器(即 Liberty 的 JAX-RS 实现代码)。
  4. 未映射的异常必须通过特定于容器的异常进行处理,然后适本地传播到底层容器 - 在本例中为 ServletException必须传递给网络容器。

JaxRsRuntimeException用于满足第4步。

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

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

  1. 您可以注册一个 ExceptionMapper特定于此异常类型( EOFException 或其任何父类(super class) - 即 IOException )。您不需要为 JaxRsRuntimeException 注册映射器因为该异常仅在 Liberty 内部使用 - 不应映射。如果您看到 JaxRsRuntimeException 传递给 ExceptionMapper ,那么您应该向 IBM 提交支持案例,因为这可能是一个错误。

带有 ExceptionMapper<EOFException>您可以在 EOFException 时返回特定响应从提供者或资源中抛出。

  1. 您可以注册自己的MessageBodyReader这会将 JSON 转换为对象(使用 Jackson 或任何其他 JSON 序列化代码),但会以您想要的方式处理空消息体 - 例如,将其转换为 null或使用某种默认对象实例。由于用户注册的提供程序优先于内置提供程序,因此将使用此 MBR 而不是 Liberty 的基于 Jackson 的 MBR。

这种方法绝对可以让您更好地控制数据的反序列化方式以及异常处理。

  1. 注册 ContainerRequestFilter当消息正文为空时将中止的提供程序。这是一个例子:

    @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());
            }
        }
    }
    

我已经使用 WebSphere Liberty 2018 年 5 月测试版成功测试了选项 1 和 3。我没有针对这种情况亲自测试选项 2,但基于过去使用自定义 MBR,这应该可行。

要记住的一件事是,当 Liberty GA 为 jaxrs-2.1功能,它将使用 JSONB 作为内置提供程序来序列化/反序列化 JSON 而不是 Jackson。我使用 JAX-RS 2.1(也在 5 月测试版中)而不是 EOFException 测试了您的场景。 , JSONB 代码抛出 NoSuchElementException .如果您认为您可能转向 JAX-RS 2.1,那么我会建议选项 2 或 3。选项 1 要求您创建一个新的 ExceptionMapper。用于 JAX-RS 2.1。

希望对您有所帮助,

安迪

关于java - Websphere Liberty 上的 JAX-RS 异常处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50446849/

有关java - Websphere Liberty 上的 JAX-RS 异常处理的更多相关文章

  1. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  2. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  3. ruby-on-rails - date_field_tag,如何设置默认日期? [ rails 上的 ruby ] - 2

    我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问

  4. ruby-on-rails - openshift 上的 rails 控制台 - 2

    我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新ruby​​gems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems

  5. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

    我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

  6. ruby-on-rails - 相关表上的范围为 "WHERE ... LIKE" - 2

    我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que

  7. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

    在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

  8. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

    我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

  9. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  10. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

随机推荐