当前位置: 首页 > 知识库问答 >
问题:

Apache camel使用HttpEndpoint选项和http4

牟辰龙
2023-03-14

我对骆驼比较陌生。我在实现以下功能时遇到了此问题。

要求:调用GET服务,如果响应的状态不是200,则需要抛出HttpOperationFailedException,以便我可以使用父路由上的OneException处理204异常。

我可以使用以下代码实现它:

from("direct:parent")
.onException(HttpOperationFailedException.class)
.onWhen(exchange ->{
    HttpOperationFailedException exe = exchange.getException(HttpOperationFailedException.class);
    if(204 == exe.getStatusCode()){
        return true;
    }
    return false;
})
.setBody(constant(null))
.end()
.to("direct:a");

from("direct:a")
.recipientList("false")
.simple("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
.convertBodyTo(String.class);

但是,使用以下代码时不会引发异常:

from("direct:parent")
.onException(HttpOperationFailedException.class)
.onWhen(exchange ->{
    HttpOperationFailedException exe = exchange.getException(HttpOperationFailedException.class);
    if(204 == exe.getStatusCode()){
        return true;
    }
    return false;
})
.setBody(constant(null))
.end()
.to("direct:a");

from("direct:a")
.to("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
.convertBodyTo(String.class);

有没有人能解释一下需要做什么改变来代替收件人列表?

共有1个答案

夏侯兴学
2023-03-14

当您将 HTTP URI 调用从直接:父级传播到直接:a,但在直接:父级中维护嵌套的异常子句时,在 direct:a 中引发的异常不会向上传播到父路由。但是,您应该做的是将嵌套的异常子句重构为全局异常子句。

我创建了一个简单的测试用例,该用例要么模拟在 204 消息上引发的异常,要么可能调用真实服务并在 204 响应的情况下失败:

import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.AdviceWithRouteBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.http.common.HttpOperationFailedException;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;

import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;

public class Http4ExceptionHandlingTest extends CamelTestSupport {

  @Produce(uri = "direct:parent")
  protected ProducerTemplate template;

  @Override
  public boolean isUseAdviceWith() {
    return true;
  }

  @Override
  protected RouteBuilder createRouteBuilder() {
    return new RouteBuilder() {
      @Override
      public void configure() throws Exception {

        onException(HttpOperationFailedException.class)
            .onWhen(exchange -> {
              HttpOperationFailedException
                  exe = exchange.getException(HttpOperationFailedException.class);
              return 204 == exe.getStatusCode();
            })
            .log("HTTP exception handled")
            .handled(true)
            //.continued(true)
            .setBody(constant(null));

        from("direct:parent").routeId("parent")
//            .onException(HttpOperationFailedException.class)
//                .onWhen(exchange -> {
//                  HttpOperationFailedException
//                      exe = exchange.getException(HttpOperationFailedException.class);
//                  return 204 == exe.getStatusCode();
//                })
//                .setBody(constant(null))
//            .end()
            .log("Parent start");
            .to("direct:a")
            .log("Parent done");

        from("direct:a").routeId("a")
            .log("a start")
            .to("http4://localhost:8022/test/service?okStatusCodeRange=200-201")
            .convertBodyTo(String.class)
            .log("a done");
      }
    };
  }

  @Test
  public void testExceptionHandling() throws Exception {
    // comment the following line out if you want to invoke the real service instead!
    weaveRoute();

    context.start();

    Object response = template.requestBody("foo");

    assertThat(response, is(nullValue()));
  }

  @Test
  public void testSuccessfulResponse() throws Exception {
    // comment the following line out if you want to invoke the real service instead!
    weaveRoute();

    context.start();

    Object response = template.requestBody("bar");

    assertThat(response, is(equalTo("bar")));
  }

  private void weaveRoute() throws Exception {
    context.getRouteDefinition("a").adviceWith(context, new AdviceWithRouteBuilder() {
      @Override
      public void configure() throws Exception {
        this.interceptSendToEndpoint("http4*")
            .skipSendToOriginalEndpoint()
            .process(exchange -> {
              String body = exchange.getIn().getBody(String.class);
              if ("foo".equals(body)) {
                Map<String, String> headers = new HashMap<>();
                String location = "";
                HttpOperationFailedException exe =
                    new HttpOperationFailedException("http://bla", 204, "No Content", location,
                                                     headers, "response body");
                throw exe;
              }
            });
      }
    });
  }
}

我在父路由中保留了原始嵌套的异常子句,以便您可以比较结果。全局异常处理程序获得了一个附加的 .handled(true)' 语句,该语句刚刚从当前路由中断。因此,文档声明如下:

如果handled为true,那么抛出的异常将被处理,Camel将不会继续在原始路由中路由,而是中断。但是,您可以在onException中配置一个将被使用的路由。如果您需要创建一些自定义响应消息返回给调用者,或者因为抛出了异常而进行任何其他处理,那么您可以使用这个路由。(来源)

未将捕获的异常设置为< code >。handled(true)将实际导致堆栈跟踪的显示,而不是继续执行。

我添加了更多的日志语句来可视化异常处理中的行为。在执行上面介绍的代码时,您将获得如下输出:

[INFO ] -  - Parent start [            ] [parent] [              ] [main] 
[INFO ] -  - a start [            ] [a] [              ] [main] 
[INFO ] -  - HTTP exception handled [            ] [a] [              ] [main] 

而不是<code>。处理(true)您也可以使用。继续(true)以继续执行记录的执行:

如果继续为真,那么骆驼将捕获异常,实际上只是忽略它并继续在原始路由中路由。但是,如果您在onException中配置了路由,它将首先路由该路由,然后再继续在原始路由中路由。

在全局异常子句中使用启用的 .continue(true) 和禁用的 .handled(true) 运行测试将生成以下日志:

[INFO ] -  - Parent start [            ] [parent] [              ] [main] 
[INFO ] -  - a start [            ] [a] [              ] [main] 
[INFO ] -  - HTTP exception handled [            ] [a] [              ] [main] 
[ERROR] -  - Failed delivery for (MessageId: ...). Exhausted after delivery attempt: 1 caught: null. Handled and continue routing.

Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId              ProcessorId          Processor                                                                        Elapsed (ms)
[parent            ] [parent            ] [direct://parent                                                               ] [         8]
[parent            ] [log9              ] [log                                                                           ] [         0]
[parent            ] [to4               ] [direct:a                                                                      ] [         8]
[a                 ] [log7              ] [log                                                                           ] [         1]
[a                 ] [to3               ] [http4://localhost:8022/test/service?okStatusCodeRange=200-201                 ] [         8]
[                  ] [process2          ] [Processor@0x3c7f66c4                                                          ] [         8]
[a                 ] [log6              ] [log                                                                           ] [         1]
[a                 ] [setBody2          ] [setBody[{null}]                                                               ] [         0]

Stacktrace
--------------------------------------------------------------------------------------------------------------------------------------- [            ] [o.a.c.p.DefaultErrorHandler] [              ] [main] 
org.apache.camel.http.common.HttpOperationFailedException: HTTP operation failed invoking http://bla with statusCode: 204, redirectLocation: 
    at at.erpel.messaginghub.services.unit.routes.rest.Http4ExceptionHandlingTest$2.lambda$configure$1(Http4ExceptionHandlingTest.java:103)
    at org.apache.camel.processor.DelegateSyncProcessor.process(DelegateSyncProcessor.java:63)
    ...
[INFO ] -  - a done [            ] [a] [              ] [main] 
[INFO ] -  - Parent done [            ] [parent] [              ] [main] 

从简化的日志中可以看出,<code>。continue(true)不会中断路由,但会记录消息历史记录以及忽略异常的堆栈跟踪。

如果父路由中有一个nesed异常子句,而子路由确实遇到异常,则向嵌套异常子句添加。处理(true)。继续(true)没有效果,因为在子路由中捕获的实际异常不会传播到父嵌套异常子句,因此根本不会处理。

我已经针对模拟和实际服务测试了代码示例,该服务在接收到foo主体时返回了204,在接收到任何其他内容时返回了200响应。<代码>?因此,okStatusCodeRange=200-201配置参数在我的情况下工作正常。对于completenes:我使用Camel 2.17.0。

 类似资料:
  • 我正在尝试向异步路由发送消息,但它不起作用。我刚刚在github上创建了一个项目来模拟这个问题

  • 这是我的骡子应用程序xml配置 获取以下异常(堆栈): 加载mule' xml:时出现异常,原因是:org . XML . sax . sax parse exception:CVC-complex-type . 2.4 . a:发现以元素' file:inbound-endpoint '开头的无效内容。应为“{ http://www . mule soft . org/schema/mule/c

  • 我想测试以下骆驼路线。我在网上找到的所有例子都有以文件开头的路由,在我的例子中,我有一个Springbean方法,每隔几分钟就会被调用一次,最后消息被转换并移动到jms以及审计目录。 我对这条路线的写测试毫无头绪。目前我在测试用例中所拥有的是

  • 问题内容: 我有以下HTML代码 我正在尝试使用Selenium获取选项值的列表(例如459、100等,而不是文本)。 目前,我有以下Python代码 如您所见,代码返回纯HTML,我正在使用HTMLParser库进行解析。有什么方法可以仅使用Selenium来获取选项值?换句话说,不必解析Selenium的结果吗? 问题答案: 检查一下,这是我做的,然后才知道选择模块做了什么 这样的输出

  • 我正在使用apache camel cxf开发一个Web服务(肥皂),我遇到了这个错误。 Java . lang . illegalargumentexception:Part { http://blue print . camel . ngt . TN/}返回的类型应为[ltn . ngt . camel . blue print . WB _ subscriptions;,而不是org . A

  • 我正在用量角器编写一些e2e测试。我的应用程序是Angular材料2应用程序。在我的测试中,我想按值选择md-select的一个选项。当我检查打开的md-select时,我看到了md-选项项。选项的值在一个属性ng-反映-值中。 假设我有一个值为“optionA”的选项。如何通过该值选择某个选项? 我试过这个: 在里面,我看到了选项的正确数目,但是我如何选择值为“of options”的选项呢?