英文:
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}}&fixedRate=true&period={{http-client.timers.http-get.period}}&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}}&fixedRate=true&period={{http-client.timers.http-post.period}}&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'm either missing some steps, or I'm doing it incorrectly.
Another thing to mention is that I'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'm having.
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 + "]";
}
}
Server
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));
}
}
Client
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);
GenericMessage<byte[]> data = new GenericMessage<>(bytes);
return data;
}
public GenericMessage<char[]> produceCharArrayData() {
int size = RandomUtil.generateInt(1000, 20000000);
char[] chars = DataGenerator.generateCharArray(size);
logger.info("produceCharArrayData: generated {} characters", size);
GenericMessage<char[]> data = new GenericMessage<>(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<?> 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<T>)
is to return null if it can't convert. Try using getMandatoryBody(Class<T>)
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.
<dataFormats>
<json id="myGeneric" .... unmarshalTypeName="....GenericMessage"/>
</dataFormats>
...
<unmarshal><custom ref="myGeneric"/></unmarshal>
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.:
<post uri="post-data" consumes="json" type="com.example.demo.GenericMessage">
You can also use the same approach to define an outType
(for marshalling the response back to JSON)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论