I have the following stream.
Context of the problem
1.
rabbit --password='******' --queues=springdataflow-q --virtual-host=springdataflow --host=172.24.172.184 --username=springdataflow | transform | httpclient --url-expression='http://172.20.24.47:8080/push' --http-method=POST --headers-expression={'Content-Type':'application/x-www-form-urlencoded'} --body-expression={arg1:payload} | log
2.
I have spring boot running locally.
#RestController
public class HelloController {
#RequestMapping(value = "/push", method = RequestMethod.POST,produces = {MediaType.TEXT_PLAIN})
public String pushMessage(#RequestParam(value="arg1") String payload) {
System.out.println(payload);
return payload;
}
}
I would like to have the rabbit message come into httpclient as value for the the 'arg1' parameter value to the post request. The intent being that message published on rabbit queue is consumed by a rest post point, the message being captured by SpEL payload.
For this I am using the body-expression = {arg1:payload} but this is not working, maybe syntactically wrong.
Any suggestions ?
The #RequestParam(value="arg1") is really about request param, the part of the URL after ?, which is called query string: https://en.wikipedia.org/wiki/Query_string.
So, if you really would like to have an arg1=payload pair in the query string, you need to use a proper url-expression:
--url-expression='http://172.20.24.47:8080/push?arg1='+payload
This seems to work to pass strings as payloads. It seems that by default the payload becomes requestbody.
So on the rest service I made a change:
#RequestMapping(value = "/pushbody", method = RequestMethod.POST,consumes = {MediaType.TEXT_PLAIN})
public String pushBody(#RequestBody String payload) {
System.out.println(payload);
return payload;
}
And the stream that seems to work now is :
rabbit --password='******' --queues=springdataflow-q1 --host=172.24.172.184 --virtual-host=springdataflow --username=springdataflow | httpclient --http-method=POST --headers-expression={'Content-Type':'text/plain'} --url=http://172.20.24.47:8080/pushbody | log
I did try with inputType= text/plain suggestion both on httpclient and logsink and removing the consumes and produces on the rest service post method, but no luck there.
Related
base on Retrofit #Field doc, when making a post request
a combination of using #FormUrlEncoded and #Field will yields a request body of: paramName=paramValue¶mName=paramValue.
but what I am not getting field paramemters included in RequestBody.
my interface definition as below:
(I have no endpoint, and jake Wharton says use ./ as explicit intent that you want to use the path of the base URL and add nothing to it, but I tried #POST("./") it's not work, i got 404 not found error, so I add full url address to bypass this error temporarily)
public interface BannerService {
#FormUrlEncoded
#POST("http://10.10.20.190:6020/router")
Flowable<List<BannerBeanList.BannerBean>> getBannerData(#Field("method") String method, #Field("adspaceId") String adspaceId);
}
and this is how I make calls to interface service:
public class RemoteListDataSource implements RemoteDataSource {
#Override
public Flowable<List<BannerBeanList.BannerBean>> getBannerListData(ADFilterType adFilterType) {
BannerService bannerService = RetrofitHttpManger.getInstance().create(BannerService.class);
return bannerService.getBannerData("mz.app.ad.list", String.valueOf(adFilterType.getValue()));
}
}
below is retrofit instance in it's private constructor
retrofit = new Retrofit.Builder()
.client(httpClientBuilder.build())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
//TODO baseurl tempororily hard code for test purpose
.baseUrl("http://10.10.20.190:6020/router/")
.build();
this is the result I got:
the current request body that I am logging is the common parameters that I added from FromBody in interceptor, only except the parameters that I passed in from #Field annoation, and server side info tells the same thing.
I have solved this issue, thanks to #iagreen's comment.
the request body was replaced by FormBody.Builder().add().build() which passed into chain.request().newBuilder().post().build() in my interceptor.
then the question turns out to be how to append paramemters in RequestBody, and the solution can refers to Retrofit2: Modifying request body in OkHttp Interceptor
I have set expectedResponseType(MyClass.class). So OutboundGateway is converting the message into my response class type and returning to me. I want to log the payload as well for debugging purpose along with the conversion.
How to do this response payload logging and conversion.?
I could do it by expecting the response as String and later convert into my class using marshallers. Is there any simpler way that can be used for all my outbound gateways?
The expectedResponseType(MyClass.class) is translated to the
httpResponse = this.restTemplate.exchange(realUri, httpMethod, httpRequest, (Class<?>) expectedResponseType);
where the last one does this:
public ResponseEntityResponseExtractor(Type responseType) {
if (responseType != null && Void.class != responseType) {
this.delegate = new HttpMessageConverterExtractor<T>(responseType,
getMessageConverters(), logger);
}
else {
this.delegate = null;
}
}
As you see it is copying its own logger to the HttpMessageConverterExtractor.
So, I think you can achieve some good result for your logging requirements switching on DEBUG (or even TRACE) for the org.springframework.web.client.RestTemplate category.
From other side you always can extend the RestTemplate a bit to make some hooks into it.
From the Spring Integration perspective we can do nothing. Because the whole hard conversion work is done in the RestTemplate.
Our javascript websocket clients adds "custom" headers to all STOMP messages.
My project handles websocket endpoints using spring-websocket #Controller.
#MessageMapping(value = "/mymessages")
public void save(#Payload ToBeSaved payload, #Headers MessageHeaders headers) {
service.save(toMsg(payload, headers));
}
protected <P> Message<P> toMsg(P payload, MessageHeaders headers) {
return MessageBuilder.createMessage(payload, headers);
}
The controller modifies the payload and then passes the new payload and original websocket headers (including the custom ones) to a spring-integration #MessagingGateway.
The underlying IntegrationFlow tries to access the "custom" headers by accessing the message headers with the SPLExpression headers['custom'].
Unfortunately headers['custom'] is always null because custom is actually contained in the nativeHeaders.
I haven't found a way to tell IntegrationFlow to look into nativeHeaders.
Is there a way in spring-websocket to copy all native headers as normal headers ?
Thanks in advance
The spring-websocket can do nothing for your on the matter. It isn't its responsibility.
If you would really like to have access to something in the nativeHeaders, you should do that manually.
For your particular case that SpEL may look like:
headers['nativeHeaders']['custom']
Because nativeHeaders is a Map as well.
From other side you can use <header-enricher> in your down stream flow to pop all those nativeHeaders to top level.
And one more point: since Spring Integration 4.2 we provide native support for STOMP adapters. And there is a StompHeaderMapper which does exactly what you want and the code there looks like:
else if (StompHeaderAccessor.NATIVE_HEADERS.equals(name)) {
MultiValueMap<String, String> multiValueMap =
headers.get(StompHeaderAccessor.NATIVE_HEADERS, MultiValueMap.class);
for (Map.Entry<String, List<String>> entry1 : multiValueMap.entrySet()) {
name = entry1.getKey();
if (shouldMapHeader(name, this.outboundHeaderNames)) {
String value = entry1.getValue().get(0);
if (StringUtils.hasText(value)) {
setStompHeader(target, name, value);
}
}
}
}
I'm trying to enrich the headers of the messages coming from an http inbound gateway ;
my uri looks like this:
requestMapping.setPathPatterns("/context/{fooId}");
But I don't know how to use the setHeaderExpressions method of the HttpRequestHandlingMessagingGateway to catch the uri variable and put its value in the header.
I have no more success with the .enrichHeaders(...) since this code generates an exception:
IntegrationFlows.from(requestNotificationChannel())
.enrichHeaders(h -> h.header("fooId", "#pathVariables.fooId")
What is the good way to extract the values from the uri-variables and/or from the parameters ?
Thanks !
Well, you missunderstood a bit how HttpRequestHandlingMessagingGateway works or we missed something in the documentaiton.
Each SpEL evaluation is done withing EvaluationContext and it is fresh for each component. The #pathVariables EvaluationContext varialbe is available only from the HttpRequestHandlingMessagingGateway during request processing. Other similar variables from the request and available for message building from the HttpRequestHandlingMessagingGateway are:
requestAttributes
requestParams
requestHeaders
cookies
matrixVariables
What I want to say that it doesn't work for regular .enrichHeaders() because it uses a new fresh EvaluationContext and all those variable aren't available already. That's why HttpRequestHandlingMessagingGateway provides setHeaderExpressions. and here is a sample how to use it for you case:
private final static SpelExpressionParser PARSER = new SpelExpressionParser();
....
#Bean
public HttpRequestHandlingMessagingGateway httpInboundGateway() {
....
httpInboundGateway.setHeaderExpressions(Collections.singletonMap("fooId", PARSER.parseExpression("#pathVariables.fooId")));
....
}
From other side, if your requestNotificationChannel() is DirectChannel, you don't leave the HTTP Request Thread in the .enrichHeaders(), therefore you can do something like this:
.enrichHeaders(h -> h.headerFunction("fooId", m ->
((Map<String, String>) RequestContextHolder.currentRequestAttributes()
.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 0)).get("fooId")))
How can I get GWT RequestFactory to with in a Gadget?
Getting GWT-RPC to work with Gadgets is explained here.
I'm looking for a analogous solution for RequestFactory.
I tried using the GadgetsRequestBuilder, so far I've managed to get the request to the server using:
requestFactory.initialize(eventBus, new DefaultRequestTransport() {
#Override
protected RequestBuilder createRequestBuilder() {
return new GadgetsRequestBuilder(RequestBuilder.POST,
getRequestUrl());
}
#Override
public String getRequestUrl() {
return "http://....com/gadgetRequest";
}
});
But I end up with the following error:
java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:694)
at com.google.gwt.autobean.server.impl.JsonSplittable.create(JsonSplittable.java:35)
at com.google.gwt.autobean.shared.impl.StringQuoter.split(StringQuoter.java:35)
at com.google.gwt.autobean.shared.AutoBeanCodex.decode(AutoBeanCodex.java:520)
at com.google.gwt.requestfactory.server.SimpleRequestProcessor.process(SimpleRequestProcessor.java:121)
The general approach for sending a RequestFactory payload should be the same as RPC. You can see the payload that's being received by the server by running it with the JVM flag -Dgwt.rpc.dumpPayload=true. My guess here is that the server is receiving a request with a zero-length payload. What happens if you set up a simple test involving a GadgetsRequestBuilder sending a POST request to your server? Do you still get the same zero-length payload behavior?