Spring Integration WebFlux outboundGateway ClientHttpResponse to Mono<Object> - spring-integration

How do we convert the Flux<DataBuffer> of ClientHttpResponse to Mono<SomeDto>. Also, can we get access to MessageHeaders in the bodyExtractor. Please suggest.
.handle(WebFlux.outboundGateway(apiUrl, webClient)
.httpMethod(POST)
.mappedRequestHeaders(CONTENT_TYPE)
.bodyExtractor((clientHttpResponse, context) -> validateAPIResponse(clientHttpResponse)))
private Mono<Object> validateAPIResponse(final ClientHttpResponse clientHttpResponse) {
var httpStatus = clientHttpResponse.getStatusCode();
// check for 500
if (httpStatus.is5xxServerError())
throw new SomeException(.......);
var responseMono = new Jackson2JsonDecoder().decodeToMono(clientHttpResponse.getBody(), ResolvableType.forClass(SomDto.class), null,
null);
// check for any other than 200
if (!httpStatus.is2xxSuccessful())
return responseMono.map(response -> {
throw new SomeOtherException(response.getSomeField(), httpStatus);
});

You can just delegate to BodyExtractors.toFlux(SomDto.class). I'm not sure if you really need a custom BodyExtractor, if just expectedResponseType(SomDto.class) should be enough for you produce such a Mono. Not sure also if you need to handle those errors yourself: the framework does a decent job on the matter.
Yes, you cannot get access to MessageHeaders in this BodyExtractor context since it has nothing to do with messaging.
You can get access to those headers a bit downstream when you already got a reply from this WebFlux gateway. If your reply is really very tied to headers, then you need to look into producing a generic reply (like that Flux<DataBuffer>) and then convert it appropriately downstream in a transformer where you already get access to the whole request message and its headers.
See also this in docs:
Spring Integration provides ClientHttpResponseBodyExtractor as a identity function to produce (downstream) the whole ClientHttpResponse and any other possible custom logic.

Related

Blue Prism web api service - Cannot send a content-body with this verb-type

I'm having some trouble getting a GET Action with body to work in BluePrism (using web api service).
It seems that when I try to include an Action that sends a GET with body when I reach that stage this error gets thrown:
Internal : Unexpected error Cannot send a content-body with this verb-type.
What I've tried:
Using a different verb-type / passing parameters in the query instead of the body, unfortunately i don't have control over the endpoint i'm trying to reach so this didnt work as it only accepts a GET containing data in the body
Using BluePrism HTTP Utility to send the call, this has the same problem as the Web API Service
Compiling the body via code instead of using a template
I haven't been able to find anyone that made it work in BluePrism and there doesn't seem to be much documentation on this issue in BluePrism so any help would be appreciated.
Thanks!
.NET's WebRequest class that Blue Prism uses under the hood to conduct
these requests will not allow you to send a request body with any GET
request. There is no (documented) way to overcome this limitation.
While other related answers on Stack
Overflow correctly state
that there exists no such prohibition on including request bodies with
GET requests per RFC
9110ยง9.3.1, it is
very unusual for a production-grade service to require that the request
itself include anything in the request body. It's also possible that
intermediaries like HTTP proxies may strip or otherwise mangle the request
body in transit anyway.
There is no out-of-the-box way to force the .NET Framework (which Blue
Prism uses) to send GET requests with a request body. If you're able,
you can install
WinHttpHandler
and implement it as a drop-in replacement for HTTPRequest (this SO
thread will help).
Because this type of solution requires the install of a new library, it's
important to consider the caveats of doing so:
Blue Prism's support for external DLLs is unstable at best, and there's
no guarantee it will even import correctly to begin with. Vendor support
for this type of setup is, anecdotally, limited to nonexistent (and
rightfully so, IMO).
If you're able to successfully implement the functionality described
with WinHttpHandler, you'll need to install it on every Blue Prism
developer's machine and runtime resource in all your environments
(development/SIT/UAT/production). For some organizations, strict IT
security posture makes this rather impractical or outright infeasible.
I managed to get it working using a code block containing C# code and using Reflection, here is a my GET method:
public string GetWithBodyAndAuth(string uri, string data, string token, out int statusCode)
{
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Headers.Add("Authorization", "Basic " + token);
request.ContentType = "application/json; charset=utf-8";
request.Method = "GET";
request.Accept = "application/json";
var type = request.GetType();
var currentMethod = type.GetProperty("CurrentMethod", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(request);
var methodType = currentMethod.GetType();
methodType.GetField("ContentBodyNotAllowed", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(currentMethod, false);
using (var streamWriter = new StreamWriter(request.GetRequestStream()))
{
streamWriter.Write(data);
}
var response = (HttpWebResponse)request.GetResponse();
StreamReader reader = new StreamReader(response.GetResponseStream());
statusCode = ((int)response.StatusCode);
return reader.ReadToEnd();
}
It's a bit of a hack and can't say if it'll be supported in the future, but for BluePrism 6.9 this allows you to send GET requests containing a body.
It has the advantage of not requiring any external DLLs, this is the import list:

How to setup ContentType for Azure ServiceBus from Spring JMS

I'm trying to use library azure-servicebus-jms-spring-boot-starter to send messages to topic. Everything works, however messages are being stored in subscriptions as application/xml type and I can't find the way how to setup this correctly to have them stored as application/json.
I've tried to configure message converter to send ContentType as described here but that doesn't work either.
#Bean
public MessageConverter jacksonJmsMessageConverter() {
final MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter(){
#Override
protected TextMessage mapToTextMessage(Object object, Session session, ObjectWriter objectWriter)
throws JMSException, IOException {
final TextMessage message = super.mapToTextMessage(object, session, objectWriter);
message.setStringProperty("ContentType", "application/json");
return message;
}
};
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
converter.setObjectMapper(objectMapper);
return converter;
}
There is no exposed means of setting the content-type on the messages sent from the Qpid JMS client. The client itself uses this field as part of the JMS mapping to AMQP to distinguish certain message types that it sends and to determine at receive time what certain messages should be presented as.
It is technically possible to use reflection to reach in and so the value but the APIs you have to use from the JmsMessageFacade class are not public and could change with any release so choosing to do so comes with significant risk.

Call POX web service from Spring Integration

I'm using Spring Integration in a project that integrates (successfully) various ReST/JSON and SOAP endpoints.
Now I need to call a BusinessWorks instance that is configured to accept Plain-Old-Xml-over-HTTP.
From the "Spring Integration in Action book", I got a hint that I should use int-ws:outbound-gateway for this.
This configuration generates the correct request, but in SOAP:
<int-ws:outbound-gateway
uri="..."
request-channel="request" reply-channel="reply"
marshaller="marshaller" unmarshaller="unmarshaller"/>
I can't figure out how to configure this to send the object in the payload as POX (no SOAP envelope).
I tried this:
<int-ws:outbound-gateway
uri="..."
request-channel="request" reply-channel="reply"
marshaller="marshaller" unmarshaller="unmarshaller"
message-factory="poxMessageFactory"/>
<bean id="poxMessageFactory"
class="org.springframework.ws.pox.dom.DomPoxMessageFactory"/>
The request seems to switch correctly to XML only but the body of the request is empty (no trace of the object present in the Spring Integration payload).
Can someone tell me what I am doing wrong or how to achieve what I am trying to do?
I think this is an omission in the AbstractWebServiceOutboundGateway:
public void doWithMessage(WebServiceMessage message) throws IOException, TransformerException {
Object payload = this.requestMessage.getPayload();
if (message instanceof SoapMessage) {
this.doWithMessageInternal(message, payload);
AbstractWebServiceOutboundGateway.this.headerMapper
.fromHeadersToRequest(this.requestMessage.getHeaders(), (SoapMessage) message);
if (this.requestCallback != null) {
this.requestCallback.doWithMessage(message);
}
}
}
Pay attention to the if (message instanceof SoapMessage) {.
So, indeed we miss there the fact that message can be different type.
Please, open JIRA bug on the matter.
Meanwhile as a workaround I would suggest you to use WebServiceTemplate directly instead of <int-ws:outbound-gateway> you can call it from the <service-activator> using marshalSendAndReceive() method for interaction.

ServiceStack.Razor CustomHttpHandler not displaying for HttpStatusCode.BadRequest

I'm trying to get a minimal app working using ServiceStack.Razor, and I'm having trouble getting a CustomHttpHandler to work. I've followed the instructions here and here, but it's not working right.
I'm using the following code to register a custom http handler for HttpStatusCode.BadRequest:
public override void Configure(Container container)
{
this.Plugins.Add(new RazorFormat());
this.SetConfig(new EndpointHostConfig
{
CustomHttpHandlers =
{
{ HttpStatusCode.NotFound, new RazorHandler("/notfound") },
{ HttpStatusCode.BadRequest, new RazorHandler("/error") }
},
DebugMode = true
});
}
The thing is, the /notfound handler works perfectly for 404s, but no matter what I do, I can't get the /error razor file to display whenever an ArgumentNullException is thrown.
My service method looks like this:
public object Any(Hello request)
{
if (string.IsNullOrEmpty(request.Name))
{
throw new ArgumentNullException("Name");
}
return new HelloResponse { Result = "Hello " + request.Name };
}
ServiceStack returns a 400 status, which is fine, but it still displays the view I have for HelloResponse:
What am I missing? Am I misunderstanding how CustomHttpHandlers are supposed to work?
For reference, I put the project up on github.
Yeah the CustomHttpHandlers are just meant for handling un-handled system generated errors. Currently they're limited to:
NotFound (404) for un-handled requests
Forbidden (403) when a request is made to an forbidden file or resource
These are the errors happen outside of ServiceStack and so isn't able to be handled by existing ServiceStack's event hooks or user-defined custom logic, so we allow users to modify the behavior in this case via CustomHttpHandlers.
The Error Handling wiki describes how to handle errors in ServiceStack.
Though it might make sense (since it's opt-in) to allow a fallback after the exception is handled to allow it to be further handled by rendering it to a user-specified page, that you're expecting to do here.
We'll look at trying to explore something like this in the future. Feel free to add future feature requests like these to ServiceStack's issue list so we don't forget.

Logging Servicestack Bad Requests

Is there a way to log the validation results which get populated in the ResponseStatus when you have the ValidationFeature plugin enabled?
From what i can understand, any requests coming in get validated and if the validation passes then it goes to the service. Using request filters i can log the requests coming in however using the response filters i can only log valid requests.
I trying to log all responses especially HttpStatus 400 (Bad request) which is returned as a result of a validation error.
I have also tried to play a bit with the RequestLog plugin but from what i understood what gets logged are only valid request (i.e requests that went to the service).
I hope you understand what i am trying to say.
Look to see how 7digital have customized ServiceStack's Validation Feature to support logging.
Related Features
The new API supports the concept of a ServiceRunner you can override to add your own event and exception hooks you can read about in the wiki.
There is also the built-in Request Logger that lets you expect details of the more recently processed requests.
Create an issue on the GitHub project if you want to see logging in the validation feature.
A quick way to log errors from the ValidationFeature plugin is simply thus:
public override void Configure(Container container)
{
...etc...
Plugins.Add(new RequestLogsFeature() { etc });
Plugins.Add(new ValidationFeature()
{
ErrorResponseFilter = MyValidationError
});
...etc...
}
public object MyValidationError(ValidationResult validationResult, object errorDto)
{
Container.Resolve<IRequestLogger>().Log(null, null, errorDto, TimeSpan.Zero);
return errorDto;
}
However, in my case, I realized that logging validation errors in the service itself is not the proper place; I instead log these errors in the applications that consume the service.

Resources