Spring Integration HttpRequestExecutingMessageHandler with url parameter expressions - spring-integration

I have a HttpRequestExecutingMessageHandler which should make HTTP GET requests to an endpoint URI and, for each request, it should pass two URL parameters with values obtained from the flowing message's headers. How can I get a Message's header values and apply them to each request made via the HttpRequestExecutingMessageHandler?
So far I have tried configuring my handler as follows:
SpelExpressionParser expressionParser = new SpelExpressionParser();
Map<String, Expression> uriVariableExpressions = new HashMap<String, Expression>(2);
uriVariableExpressions.put("userId", expressionParser.parseExpression("headers.userId"));
uriVariableExpressions.put("roleId", expressionParser.parseExpression("headers.roleId"));
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(uri);
handler.setHttpMethod(HttpMethod.GET);
handler.setUriVariableExpressions(uriVariableExpressions);
but, when a message flows through and the HTTP request is made, the Message's userId and roleId header values are not set as parameters in the request's URL. When debugging, I can see that the Message's headers and values are definitely in the flowing Messages. Is the spel expression correct?
Thanks,
PM

Your uri has to have placeholders to substitute your variables, e.g.:
http://foo.com/service?userId={userId}&roleId={roleId}
From other side, show, please, your uri and the logs, when you send a message.

Related

Scrapy - is it possible to extract Payload Request from Response

is it possible to extract and set as variable the "Payload Request" which has been pushed in order to receive particular response?
You can access the request object in the callback function by response.request.
This object is the request object itself, so it contains everything you passed in the request. It doesn't have a "payload" attribute though.
The equivalent should be response.request.body, assuming you had a body in the request. Everything else is still there, headers, cookies, meta, method, etc
More on the params of request here.

Why is the request body blank when the content-type is application/x-www-form-urlencoded?

I am receiving a request with content-type application/x-www-form-urlencoded. When I try to read the body of the request using cherrypy.request.body.read() the results is b''.
I seem to be able to access the request form parameters using any of these:
cherrypy.request.params
cherrypy.request.body.params
cherrypy.request.body.request_params
But this is inconvenient for my use case, I want to be able to obtain the raw request body regardless of content-type. Also the above 3 give me a dictionary, which isn't the exact format that the request had in its body. Is there a way to do that with cherrypy? Or is this functionality hidden?
Not sure what are you trying to accomplish by not using the already parsed body that correspond to the defined Content-Type... but you can process the body of the request yourself configuring: cherrypy.request.process_request_body = False and read the body with something like:
cherrypy.request.rfile.read(cherrypy.request.headers['Content-Length'])
For more information see: https://github.com/cherrypy/cherrypy/blob/master/cherrypy/_cprequest.py#L292-L315
Fragment of relevant parts of that url:
rfile = None
"""
If the request included an entity (body), it will be available
as a stream in this attribute. However, the rfile will normally
be read for you between the 'before_request_body' hook and the
'before_handler' hook, and the resulting string is placed into
either request.params or the request.body attribute.
You may disable the automatic consumption of the rfile by setting
request.process_request_body to False, either in config for the desired
path, or in an 'on_start_resource' or 'before_request_body' hook.
WARNING: In almost every case, you should not attempt to read from the
rfile stream after CherryPy's automatic mechanism has read it. If you
turn off the automatic parsing of rfile, you should read exactly the
number of bytes specified in request.headers['Content-Length'].
Ignoring either of these warnings may result in a hung request thread
or in corruption of the next (pipelined) request.
"""
process_request_body = True
"""
If True, the rfile (if any) is automatically read and parsed,
and the result placed into request.params or request.body.
"""
body = None
"""
If the request Content-Type is 'application/x-www-form-urlencoded'
or multipart, this will be None. Otherwise, this will be an instance
of :class:`RequestBody<cherrypy._cpreqbody.RequestBody>` (which you
can .read()); this value is set between the 'before_request_body' and
'before_handler' hooks (assuming that process_request_body is True."""

Picking the Right SoapAction when using MarshallingWebServiceOutboundGateway

I am trying to learn Spring Integration. As a test project to explore all the features. My project goes like this
AMQP Queue -> Header Router -> Transformer -> OutBound SOAP WS Call -> Log Results -> End
1.) Start the flow by Listening to a RabbitMq Queue. The RabbitMq Message that are sent will have a String Value (Example: 32) and a Header like {"operation" "CelsiusToFahrenheit"}.
2.) The RabbitMq message is then transformed to the corresponding Java Object based on the RabbitMQ Header Value ("operation").
3.) Then I am using a MarshallingWebServiceOutboundGateway to invoke the SOAP WebService # (http://www.w3schools.com/xml/tempconvert.asmx). When I do that I am getting an Error.
ErrorMessage [payload=org.springframework.messaging.MessageHandlingException: error occurred in message handler [wsOutboundGateway]; nested exception is org.springframework.ws.soap.client.SoapFaultClientException: Server did not recognize the value of HTTP Header SOAPAction: ., headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#601affec, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#601affec, id=7f6624cc-e7a4-37f6-0a1f-578dc78c4afa, timestamp=1482356790717}]
As you can see the SOAPAction was Blank and the server did NOT like it.
If I hard code the SOAPAction like this
public MessageHandler wsOutboundGateway()
{
MarshallingWebServiceOutboundGateway temperatureConversionWebServiceGateway = new MarshallingWebServiceOutboundGateway(WEBSERVICE_URL, jaxb2Marshaller(), jaxb2Marshaller());
temperatureConversionWebServiceGateway.setRequestCallback(new SoapActionCallback("http://www.w3schools.com/xml/CelsiusToFahrenheit"));
temperatureConversionWebServiceGateway.setOutputChannelName("webServiceResponseChannel");
return temperatureConversionWebServiceGateway;
}
it works!
However, the endpoint exposes 2 Services (2 Possible Actions). See below.
FahrenheitToCelsius soapAction="http://www.w3schools.com/xml/FahrenheitToCelsius"
CelsiusToFahrenheit soapAction="http://www.w3schools.com/xml/CelsiusToFahrenheit"
I want to be able to set the SoapAction at run time based on the RabbitMQ Header Value ("operation"). How do I do it?
On a Side note, I have done a few different experiments to solve this problem but none seems to have yielded the results I am looking for. One of the solutions I tried was to set the SoapAction as part of the Transformation process (Converting the Input from Rabbit MQ message to the corresponding Java Type based on the RabbitMQ Header Value) and send that request to the Outbound Gateway (See Code below).
#Bean
public IntegrationFlow generateCelsiusToFahrenheitRequestFlow() {
Map<String, Object> headers = new HashMap<>();
headers.put("SOAPAction", "http://www.w3schools.com/xml/CelsiusToFahrenheit");
return IntegrationFlows.from(celsiusToFahrenheitChannel)
.transform(celsiusToFahrenheitTransformer)
.enrichHeaders(headers)
.channel(webServiceRequestChannel)
.get();
}
After I do that and tap the 'webServiceRequestChannel', I see the SoapAction Header that I added previously (See Below).
GenericMessage [payload=CelsiusToFahrenheit [celsius=32], headers={SOAPAction=http://www.w3schools.com/xml/CelsiusToFahrenheit, amqp_receivedDeliveryMode=NON_PERSISTENT,........
However, when the MarshallingWebServiceOutboundGateway picked up the request and makes the outbound call to the SOAP WebService, the "SOAPAction: " is still Blank. I am not sure why the Soap Action got blanked out. What am I missing?
Sorry if I have not followed conventions when Writing / Formatting my question. This is my first time here on Stack Overflow. Any inputs or suggestions to address this issues will be greatly appriciated.
Yeah, the mistake here that the header name must be exactly WebServiceHeaders.SOAP_ACTION, where its name is ws_soapAction. Only now a DefaultSoapHeaderMapper is able to map it into request WebserviceMessage:
public DefaultSoapHeaderMapper() {
super(WebServiceHeaders.PREFIX, STANDARD_HEADER_NAMES, Collections.<String>emptyList());
}

Web API action filter content can't be read

Related question: Web API action parameter is intermittently null and http://social.msdn.microsoft.com/Forums/vstudio/en-US/25753b53-95b3-4252-b034-7e086341ad20/web-api-action-parameter-is-intermittently-null
Hi!
I'm creating a ActionFilterAttribute in ASP.Net MVC WebAPI 4 so I can apply the attribute in action methods at the controller that we need validation of a token before execute it as the following code:
public class TokenValidationAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
//Tried this way
var result = string.Empty;
filterContext.Request.Content.ReadAsStringAsync().ContinueWith((r)=> content = r.Result);
//And this
var result = filterContext.Request.Content.ReadAsStringAsync().Result;
//And this
var bytes = await request.Content.ReadAsByteArrayAsync().Result;
var str = System.Text.Encoding.UTF8.GetString(bytes);
//omit the other code that use this string below here for simplicity
}
}
I'm trying to read the content as string. Tried the 3 ways as stated in this code, and all of them return empty. I know that in WebApi I can read only once the body content of the request, so I'm commenting everything else in the code, and trying to get it run to see if I'm getting a result. The point is, the client and even the Fiddler, reports the 315 of the content length of the request. The same size is getting on the server content header as well but, when we try read the content, it is empty.
If I remove the attribute and make the same request, the controller is called well, and the deserialization of Json happens flawless. If I put the attribute, all I get is a empty string from the content. It happens ALWAYS. Not intermittent as the related questions state.
What am I doing wrong? Keep in mind that I'm using ActionFilter instead of DelegatingHandler because only selected actions requires the token validation prior to execution.
Thanks for help! I really appreciate it.
Regards...
Gutemberg
By default the buffer policy for Web Host(IIS) scenarios is that the incoming request's stream is always buffered. You can take a look at System.Web.Http.WebHost.WebHostBufferPolicySelector. Now as you have figured, Web Api's formatters will consume the stream and will not try to rewind it back. This is on purpose because one could change the buffer policy to make the incoming request's stream to be non-buffered in which case the rewinding would fail.
So in your case, since you know that the request is going to be always buffered, you could get hold of the incoming stream like below and rewind it.
Stream reqStream = await request.Content.ReadAsStreamAsync();
if(reqStream.CanSeek)
{
reqStream.Position = 0;
}
//now try to read the content as string
string data = await request.Content.ReadAsStringAsync();

Default Content Type + Deserialization errors

I have configured my AppHost with JSON as the default content type. Unfortunately in situations when ServiceStack fails to deserialize the request into the corresponding DTO it responds with an application/xml content. Here's an example response (I've omitted most of the message and stacktrace element values for brevity):
<UpsertResponse xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="">
<ResponseStatus>
<ErrorCode>SerializationException</ErrorCode>
<Message>Could not deserialize 'application/xml' request using ...</Message>
<StackTrace>at ServiceStack.WebHost.Endpoints.Support.EndpointHandlerBase.CreateContentTypeRequest(IHttpRequest httpReq, Type requestType, String contentType)...</StackTrace>
</ResponseStatus>
</UpsertResponse>
I guess the response is encoded that way because the request was sending 'application/xml' content. Is there a way to force (or hook into) ServiceStack to use the default content type?
A bit more conext - not only does my service uses JSON as the default content type, but it (tries to) ensures that JSON is the only accepted content type via a global request filter, which checks Accept/Content-type headers during each read/write and short-circuits the request with a 400 response when the content type is not 'application/json'.

Resources