Apache Camel:发送请求主体的问题POSTing

huangapple go评论73阅读模式
英文:

Apache Camel: Problem POSTing request body

问题

以下是您提供的内容的翻译:

我有一个服务器和客户端应用程序,它们都使用Apache Camel来配置和管理它们的路由。我正在尝试使用REST组件从客户端向服务器发起POST请求,并在请求体中发送消息。似乎REST端点已被调用,但请求体未传递。我认为我可能要么漏掉了一些步骤,要么我做错了。

另一个要提到的事情是,我正在使用一个通用的消息类,实际上可以包含任何对象(包括字节数组或字符数组)。也许这是我遇到问题的原因的一部分(或者可能是全部)。

GenericMessage.java:

public class GenericMessage<T extends Object> {
  private T payload;

  public GenericMessage(T payload) {
    this.payload = payload;
  }

  public T getPayload() {
    return payload;
  }

  public void setPayload(T payload) {
    this.payload = payload;
  }

  @Override
  public String toString() {
    return "GenericMessage [payload=" + payload + "]";
  }
}

服务器

camel-context.xml:

<camelContext id="camel-context" xmlns="http://camel.apache.org/schema/spring">
  <restConfiguration component="jetty" bindingMode="json"
    contextPath="/" host="localhost" port="9100">
    <dataFormatProperty key="prettyPrint" value="true" />
  </restConfiguration>

  <rest>
    <get uri="/data?size={size}" produces="JSON">
      <route id="get-data-by-size">
        <log loggingLevel="INFO" message="start - get-data-by-size" />
        <process ref="get-request-processor" />
        <log loggingLevel="INFO" message="end - get-data-by-size" />
      </route>
    </get>

    <post uri="post-data" consumes="json">
      <route id="post-data">
        <log loggingLevel="INFO" message="start - post-data" />
        <process ref="post-request-processor" />
        <log loggingLevel="INFO" message="end - post-data" />
      </route>
    </post>
  </rest>
</camelContext>

GetRequestProcessor.java:

@Component("get-request-processor")
public class GetRequestProcessor implements Processor {
  @Autowired
  private DataProvider provider;

  public void process(Exchange exchange) throws Exception {
    int size = exchange.getIn().getHeader("size", Integer.class);
    // TODO currently only sends char[] data
    GenericMessage<?> data = provider.getCharData(size);
    exchange.getMessage().setBody(data, GenericMessage.class);
  }
}

PostRequestProcessor.java:

@Component("post-request-processor")
public class PostRequestProcessor implements Processor {
  private final Logger logger = LoggerFactory.getLogger(PostRequestProcessor.class);

  public void process(Exchange exchange) throws Exception {
    GenericMessage<?> data = exchange.getIn().getBody(GenericMessage.class);

    if (data != null) {
      logger.info("process: object received: {}", data);
    } else {
      logger.warn("process: null object");
    }
  }
}

DataProvider.java:

@Component
public class DataProvider {
  private final Logger logger = LoggerFactory.getLogger(DataProvider.class);

  public GenericMessage<?> getByteData(final int size) {
    logger.info("getByteData: size={}", size);
    return new GenericMessage<byte[]>(DataGenerator.generateByteArray(size));
  }

  public GenericMessage<?> getCharData(final int size) {
    logger.info("getCharData: size={}", size);
    return new GenericMessage<char[]>(DataGenerator.generateCharArray(size));
  }
}

客户端

camel-context.xml:

<camelContext id="camel-context" xmlns="http://camel.apache.org/schema/spring">
  <restConfiguration component="jetty" bindingMode="json"
    contextPath="/" host="{{http-client.server.host}}"
    port="{{http-client.server.port}}">
    <dataFormatProperty key="prettyPrint" value="true" />
  </restConfiguration>

  <route id="rest-get">
    <from
      uri="timer:{{http-client.timers.http-get.name}}?delay={{http-client.timers.http-get.start-delay}}&amp;fixedRate=true&amp;period={{http-client.timers.http-get.period}}&amp;repeatCount={{http-client.timers.http-get.repeat-count}}" />
    <log loggingLevel="INFO" message="start - rest-get" />
    <to uri="rest:get:{{http-client.endpoints.http-get}}" />
    <process ref="process-get-response" />
    <log loggingLevel="INFO" message="end - rest-get" />
  </route>

  <route id="rest-post">
    <from
      uri="timer:{{http-client.timers.http-post.name}}?delay={{http-client.timers.http-post.start-delay}}&amp;fixedRate=true&amp;period={{http-client.timers.http-post.period}}&amp;repeatCount={{http-client.timers.http-post.repeat-count}}" />
    <log loggingLevel="INFO" message="start - rest-post" />
    <process ref="add-post-body" />
    <to uri="rest:post:{{http-client.endpoints.http-post}}" />
    <log loggingLevel="INFO" message="end - rest-post" />
  </route>
</camelContext>

AddPostRequestBody.java:

@Component("add-post-body")
public class AddPostRequestBody implements Processor {
  private final Logger logger = LoggerFactory.getLogger(AddPostRequestBody.class);

  @Autowired
  private DataProvider provider;

  @Override
  public void process(Exchange exchange) throws Exception {
    GenericMessage<?> data = null;
    int intValue = RandomUtil.generateInt(1);

    switch (intValue) {
      case 0:
        data = provider.produceByteArrayData();
        break;
      case 1:
        data = provider.produceCharArrayData();
        break;
      default:
        break;
    }

    logger.info("adding POST request body:\n{}", data);

    exchange.getMessage().setHeader(Exchange.HTTP_METHOD, "POST");
    exchange.getMessage().setHeader(Exchange.CONTENT_TYPE, "application/json");
    exchange.getMessage().setBody(data, GenericMessage.class);
  }
}

GetResponseProcessor.java:

@Component("process-get-response")
public class GetResponseProcessor implements Processor {
  private final Logger logger = LoggerFactory.getLogger(GetResponseProcessor.class);

  @Override
  public void process(Exchange exchange) throws Exception {
    GenericMessage<?> body = (GenericMessage<?>) exchange.getIn().getBody();
    logger.info("body: {}", body);
  }
}

DataProvider.java:

@Component
public class DataProvider {
  private final Logger logger = LoggerFactory.getLogger(DataProvider.class);

  public GenericMessage<byte[]> produceByteArrayData() {
    int size = RandomUtil.generateInt(1000, 20000000);
    byte[] bytes = DataGenerator.generateByteArray(size);
    logger.info("produceByteArrayData: generated {} bytes", size);


<details>
<summary>英文:</summary>

I have a server and client application that both use Apache Camel to configure and manage their routing. I am trying to use the REST component to make a POST request from the client to the server, and send a message in the request body. It seems that the REST endpoint is being invoked, but the body is not making it through. I assume I&#39;m either missing some steps, or I&#39;m doing it incorrectly.

Another thing to mention is that I&#39;m using a generic message class that can essentially contain any object (including arrays of bytes or chars). Perhaps this is part (or maybe all) of the cause behind the problems I&#39;m having.

GenericMessage.java:

    public class GenericMessage&lt;T extends Object&gt; {
      private T payload;
    
      public GenericMessage(T payload) {
        this.payload = payload;
      }
    
      public T getPayload() {
        return payload;
      }
    
      public void setPayload(T payload) {
        this.payload = payload;
      }
    
      @Override
      public String toString() {
        return &quot;GenericMessage [payload=&quot; + payload + &quot;]&quot;;
      }
    }

Server

camel-context.xml:

      &lt;camelContext id=&quot;camel-context&quot; xmlns=&quot;http://camel.apache.org/schema/spring&quot;&gt;
        &lt;restConfiguration component=&quot;jetty&quot; bindingMode=&quot;json&quot;
          contextPath=&quot;/&quot; host=&quot;localhost&quot; port=&quot;9100&quot;&gt;
          &lt;dataFormatProperty key=&quot;prettyPrint&quot; value=&quot;true&quot; /&gt;
        &lt;/restConfiguration&gt;
    
        &lt;rest&gt;
          &lt;get uri=&quot;/data?size={size}&quot; produces=&quot;JSON&quot;&gt;
            &lt;route id=&quot;get-data-by-size&quot;&gt;
              &lt;log loggingLevel=&quot;INFO&quot; message=&quot;start - get-data-by-size&quot; /&gt;
              &lt;process ref=&quot;get-request-processor&quot; /&gt;
              &lt;log loggingLevel=&quot;INFO&quot; message=&quot;end - get-data-by-size&quot; /&gt;
            &lt;/route&gt;
          &lt;/get&gt;
    
          &lt;post uri=&quot;post-data&quot; consumes=&quot;json&quot;&gt;
            &lt;route id=&quot;post-data&quot;&gt;
              &lt;log loggingLevel=&quot;INFO&quot; message=&quot;start - post-data&quot; /&gt;
              &lt;process ref=&quot;post-request-processor&quot; /&gt;
              &lt;log loggingLevel=&quot;INFO&quot; message=&quot;end - post-data&quot; /&gt;
            &lt;/route&gt;
          &lt;/post&gt;
        &lt;/rest&gt;
      &lt;/camelContext&gt;

GetRequestProcessor.java:

    @Component(&quot;get-request-processor&quot;)
    public class GetRequestProcessor implements Processor {
      @Autowired
      private DataProvider provider;
    
      public void process(Exchange exchange) throws Exception {
        int size = exchange.getIn().getHeader(&quot;size&quot;, Integer.class);
        // TODO currently only sends char[] data
        GenericMessage&lt;?&gt; data = provider.getCharData(size);
        exchange.getMessage().setBody(data, GenericMessage.class);
      }
    }

PostRequestProcessor.java:

    @Component(&quot;post-request-processor&quot;)
    public class PostRequestProcessor implements Processor {
      private final Logger logger = LoggerFactory.getLogger(PostRequestProcessor.class);
    
      public void process(Exchange exchange) throws Exception {
        GenericMessage&lt;?&gt; data = exchange.getIn().getBody(GenericMessage.class);
    
        if (data != null) {
          logger.info(&quot;process: object received: {}&quot;, data);
        } else {
          logger.warn(&quot;process: null object&quot;);
        }
      }
    }

DataProvider.java:

    @Component
    public class DataProvider {
      private final Logger logger = LoggerFactory.getLogger(DataProvider.class);
    
      public GenericMessage&lt;?&gt; getByteData(final int size) {
        logger.info(&quot;getByteData: size={}&quot;, size);
        return new GenericMessage&lt;byte[]&gt;(DataGenerator.generateByteArray(size));
      }
    
      public GenericMessage&lt;?&gt; getCharData(final int size) {
        logger.info(&quot;getCharData: size={}&quot;, size);
        return new GenericMessage&lt;char[]&gt;(DataGenerator.generateCharArray(size));
      }
    }

Client

camel-context.xml:

      &lt;camelContext id=&quot;camel-context&quot; xmlns=&quot;http://camel.apache.org/schema/spring&quot;&gt;
        &lt;restConfiguration component=&quot;jetty&quot; bindingMode=&quot;json&quot;
          contextPath=&quot;/&quot; host=&quot;{{http-client.server.host}}&quot;
          port=&quot;{{http-client.server.port}}&quot;&gt;
          &lt;dataFormatProperty key=&quot;prettyPrint&quot; value=&quot;true&quot; /&gt;
        &lt;/restConfiguration&gt;
    
        &lt;route id=&quot;rest-get&quot;&gt;
          &lt;from
            uri=&quot;timer:{{http-client.timers.http-get.name}}?delay={{http-client.timers.http-get.start-delay}}&amp;amp;fixedRate=true&amp;amp;period={{http-client.timers.http-get.period}}&amp;amp;repeatCount={{http-client.timers.http-get.repeat-count}}&quot; /&gt;
          &lt;log loggingLevel=&quot;INFO&quot; message=&quot;start - rest-get&quot; /&gt;
          &lt;to uri=&quot;rest:get:{{http-client.endpoints.http-get}}&quot; /&gt;
          &lt;process ref=&quot;process-get-response&quot; /&gt;
          &lt;log loggingLevel=&quot;INFO&quot; message=&quot;end - rest-get&quot; /&gt;
        &lt;/route&gt;
    
        &lt;route id=&quot;rest-post&quot;&gt;
          &lt;from
            uri=&quot;timer:{{http-client.timers.http-post.name}}?delay={{http-client.timers.http-post.start-delay}}&amp;amp;fixedRate=true&amp;amp;period={{http-client.timers.http-post.period}}&amp;amp;repeatCount={{http-client.timers.http-post.repeat-count}}&quot; /&gt;
          &lt;log loggingLevel=&quot;INFO&quot; message=&quot;start - rest-post&quot; /&gt;
          &lt;process ref=&quot;add-post-body&quot; /&gt;
          &lt;to uri=&quot;rest:post:{{http-client.endpoints.http-post}}&quot; /&gt;
          &lt;log loggingLevel=&quot;INFO&quot; message=&quot;end - rest-post&quot; /&gt;
        &lt;/route&gt;
      &lt;/camelContext&gt;

AddPostRequestBody.java:

    @Component(&quot;add-post-body&quot;)
    public class AddPostRequestBody implements Processor {
      private final Logger logger = LoggerFactory.getLogger(AddPostRequestBody.class);
    
      @Autowired
      private DataProvider provider;
    
      @Override
      public void process(Exchange exchange) throws Exception {
        GenericMessage&lt;?&gt; data = null;
        int intValue = RandomUtil.generateInt(1);
    
        switch (intValue) {
          case 0:
            data = provider.produceByteArrayData();
            break;
          case 1:
            data = provider.produceCharArrayData();
            break;
          default:
            break;
        }
    
        logger.info(&quot;adding POST request body:\n{}&quot;, data);
    
        exchange.getMessage().setHeader(Exchange.HTTP_METHOD, &quot;POST&quot;);
        exchange.getMessage().setHeader(Exchange.CONTENT_TYPE, &quot;application/json&quot;);
        exchange.getMessage().setBody(data, GenericMessage.class);
      }
    }

GetResponseProcessor.java:

    @Component(&quot;process-get-response&quot;)
    public class GetResponseProcessor implements Processor {
      private final Logger logger = LoggerFactory.getLogger(GetResponseProcessor.class);
    
      @Override
      public void process(Exchange exchange) throws Exception {
        GenericMessage&lt;?&gt; body = (GenericMessage&lt;?&gt;) exchange.getIn().getBody();
        logger.info(&quot;body: {}&quot;, body);
      }
    }

DataProvider.java:

    @Component
    public class DataProvider {
      private final Logger logger = LoggerFactory.getLogger(DataProvider.class);
    
      public GenericMessage&lt;byte[]&gt; produceByteArrayData() {
        int size = RandomUtil.generateInt(1000, 20000000);
        byte[] bytes = DataGenerator.generateByteArray(size);
        logger.info(&quot;produceByteArrayData: generated {} bytes&quot;, size);
        GenericMessage&lt;byte[]&gt; data = new GenericMessage&lt;&gt;(bytes);
        return data;
      }
    
      public GenericMessage&lt;char[]&gt; produceCharArrayData() {
        int size = RandomUtil.generateInt(1000, 20000000);
        char[] chars = DataGenerator.generateCharArray(size);
        logger.info(&quot;produceCharArrayData: generated {} characters&quot;, size);
        GenericMessage&lt;char[]&gt; data = new GenericMessage&lt;&gt;(chars);
        return data;
      }
    }

I have it configured to do one POST request from the client. There are logging statements scattered about to give an idea what is going on.

Client console:

    2020-04-13 13:23:39.805  INFO 17464 --- [           main] o.a.c.i.e.AbstractCamelContext           : Route: rest-get started and consuming from: timer://http-get
    2020-04-13 13:23:39.806  INFO 17464 --- [           main] o.a.c.i.e.AbstractCamelContext           : Route: rest-post started and consuming from: timer://http-post
    2020-04-13 13:23:39.812  INFO 17464 --- [           main] o.a.c.i.e.AbstractCamelContext           : Total 2 routes, of which 2 are started
    2020-04-13 13:23:39.812  INFO 17464 --- [           main] o.a.c.i.e.AbstractCamelContext           : Apache Camel 3.1.0 (CamelContext: RESTClient) started in 1.679 seconds
    2020-04-13 13:23:42.308  INFO 17464 --- [mer://http-post] rest-post                                : start - rest-post
    2020-04-13 13:23:42.312  INFO 17464 --- [mer://http-post] e.m.l.m.s.DataProvider                   : produceCharArrayData: generated 1039 characters
    2020-04-13 13:23:42.313  INFO 17464 --- [mer://http-post] e.m.l.m.p.AddPostRequestBody             : CLIENT: Adding POST request body:
    GenericMessage [payload=[C@2124c204]
    2020-04-13 13:23:42.408  INFO 17464 --- [mer://http-post] rest-post                                : end - rest-post

Server console:

    2020-04-13 13:23:10.846  INFO 23260 --- [           main] o.a.c.i.e.AbstractCamelContext           : Route: get-data-by-size started and consuming from: jetty:http://localhost:9100/data
    2020-04-13 13:23:10.847  INFO 23260 --- [           main] o.a.c.i.e.AbstractCamelContext           : Route: post-data started and consuming from: jetty:http://localhost:9100/post-data
    2020-04-13 13:23:10.852  INFO 23260 --- [           main] o.a.c.i.e.AbstractCamelContext           : Total 2 routes, of which 2 are started
    2020-04-13 13:23:10.853  INFO 23260 --- [           main] o.a.c.i.e.AbstractCamelContext           : Apache Camel 3.1.0 (CamelContext: RESTServer) started in 0.314 seconds
    2020-04-13 13:23:42.395  INFO 23260 --- [tp1276761134-37] post-data                                : start - post-data
    2020-04-13 13:23:42.396  INFO 23260 --- [tp1276761134-37] e.m.l.m.p.PostRequestProcessor           : SERVER: Processing POST request
    2020-04-13 13:23:42.400  WARN 23260 --- [tp1276761134-37] e.m.l.m.p.PostRequestProcessor           : null object
    2020-04-13 13:23:42.400  INFO 23260 --- [tp1276761134-37] post-data                                : end - post-data


</details>


# 答案1
**得分**: 2

以下是翻译好的内容

Camel并不了解你的`GenericMessage`因此不知道如何进行转换

Camel使用可插拔的[类型转换器][2]来告诉它如何在不同类型的对象之间转换消息主体)。

内置的类型转换器涵盖了许多常见情况例如从字符串到整数从迭代器到ArrayList等),但它们不知道如何与你编写的任何自定义类进行转换

这就是为什么服务器处理器中的这行代码返回null的原因

```java
GenericMessage<?> data = exchange.getIn().getBody(GenericMessage.class);

因为在此时,主体是一个流,但Camel不知道如何将其转换为你要求的GenericMessage

getBody(Class<T>)的默认行为是如果无法进行转换,则返回_null_。如果您希望抛出异常,请尝试使用getMandatoryBody(Class<T>)。)

在客户端:如何将响应作为GenericMessage获取?你需要使用_解组_。首先,你将JSON解组为Java对象。在这一点上,你需要向Camel提供一个提示,告诉它解组为哪个类。

<dataFormats>
    <json id="myGeneric" .... unmarshalTypeName="....GenericMessage"/>
</dataFormats>

...
<unmarshal><custom ref="myGeneric"/></unmarshal>

然后主体将是你的GenericMessage类型。

请注意,在Camel中,类型转换和编组/解组是两个不同的概念。

**在服务器端:**Camel将自动尝试将请求从JSON解组为简单的Java对象(例如HashMap等)。要告诉它解组为特定的POJO,请在REST动词定义上设置一个type,例如:

<post uri="post-data" consumes="json" type="com.example.demo.GenericMessage">

您还可以使用相同的方法来定义outType(用于将响应编组回JSON)

英文:

Camel doesn't know your GenericMessage class, so it doesn't know how to convert to/from it.

Camel uses pluggable type converters to tell it how to convert a message (Body) between different types of objects.

The built-in type converters cover a lot of common cases (e.g. String to Integer, Iterator to ArrayList, etc), but they don't know how to convert to or from any custom classes you write.

That is why this line in the Server Processor returns null:

GenericMessage&lt;?&gt; data = exchange.getIn().getBody(GenericMessage.class);

Because at the time, the Body is a Stream, but Camel doesn't know how to convert it to the GenericMessage you've asked for.

(The default behaviour of getBody(Class&lt;T&gt;) is to return null if it can't convert. Try using getMandatoryBody(Class&lt;T&gt;) if you want an Exception to be thrown instead.)

On the client side: how do you get the response as a GenericMessage? You need to use unmarshalling. You unmarshal the JSON first, into a Java object. It's at this point that you need to give Camel a hint of which class to unmarshal to.

&lt;dataFormats&gt;
&lt;json id=&quot;myGeneric&quot; .... unmarshalTypeName=&quot;....GenericMessage&quot;/&gt;
&lt;/dataFormats&gt;
...
&lt;unmarshal&gt;&lt;custom ref=&quot;myGeneric&quot;/&gt;&lt;/unmarshal&gt;

Then the Body will be your GenericMessage type.

Note that in Camel, type conversion and marshalling/unmarshalling are two different things.

On the Server side: Camel will try to unmarshal a request from JSON to a simple Java object (e.g. a HashMap, etc.) automatically. To tell it to unmarshal to a specific POJO, set a type on the REST verb definition, e.g.:

&lt;post uri=&quot;post-data&quot; consumes=&quot;json&quot; type=&quot;com.example.demo.GenericMessage&quot;&gt;

You can also use the same approach to define an outType (for marshalling the response back to JSON)

huangapple
  • 本文由 发表于 2020年4月11日 02:52:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/61146802.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定