Spring WebFlux WebClient builder set request body - spring

How do we set the request body in case of WebClient.Builder? Here is my code -
WebClient.Builder webClientBuilder = WebClient.builder().baseUrl(clientMetadataServiceUri).defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
});
webClientBuilder.clientConnector(getHttpConnector()).build().get().exchange().doOnSuccess(clientResponse -> {...})
Where and how should I add the request body here?

I believe it cannot be done.
Generally, WebClient(or RestTemplate) is like a template which you use to call other Rest Service. You define this template once with all the customizations needed like interceptors,messageConverters, errorHandlers etc which you need to communicate with this particular Service.
Now coming to individual calls to the service, each call to the service may vary. For example you might be calling different methods like Get, Post.. etc. You might call different endpoints. You might call with/without body. Since you always use the same client(WebClient/RestTemplate) to communicate with that service, you cannot create a WebClient instance with body or method or url(you can only set baseUrl) which are specific to individual call.
This is similar to RestTemplateBuilder. You cannot find any method to set either endpoint or method or body.
You might create a separate instance of webclient for each call. But that is not how it is generally used or advisable(Generally you define a bean of type WebClient and Autowire it). Hence it is not available.

The issue is with get() like many other frameworks Spring WebFlux also doesn't support the request body for the get calls. In the case of post, it goes like this -
WebClient.Builder webClientBuilder = WebClient.builder().baseUrl(clientMetadataServiceUri).defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
});
webClientBuilder.clientConnector(getHttpConnector()).build().post().body(...).exchange().doOnSuccess(clientResponse -> {...})

Related

Ways to handle exceptions like WebClientRequestException due to service unavailability for all calls from a WebClient instance than individually

As the title suggests, I'm using Spring WebClient to invoke an external api and process the response. I have added a ExchangeFilterFunction to handle the response based on the status code returned from the server as something like below.
ExchangeFilterFunction responseProcessor() {
return ExchangeFilterFunction.ofResponseProcessor(response -> {
if (response.statusCode().isError()) {
return Mono.error(new RuntimeException("WebClient Error"));
}
return Mono.just(response);
});
}
Now, this works fine with services that return a response for the request when it is up. But when the service is down, the request fails with WebClientRequestException which is fine but the error thrown is not handled by the responseProcessor and gets propagated.
I'm aware that the error can be handled on the WebClient call using any of the onErrorXXX methods. But if we use that WebClient instance to make many calls across different parts of the code, the handling looks inefficient. So, I'd like to know whether there's a way to handle this error for all calls done by that webclient instance instead of handling it in all of individual invocations. Something like what the responseProcessor does for all responses.
when you use .exchangeToMono() or .exchangeToFlux() methods it will give you back a mono or flux .
there is an interesting method on mono or flux which is transform() .
transform take a function an add it to your flux or mono chain.
you can define a chain as a function anywhere and use this chain many time in different chains.
so define your exception handling chain ( by using onError() method ) as a function and after you get your response by exchangeToMono() or exchangeToFlux() , use .transform method and pass your exception handling chain to it.
any way , there is another way too .
you can create a method which is your proxy to call any external resource , which use webClient in itself.
then you can apply AOP pattern to it and handle exception in this way.

Programmatically invoking a Jersey ResourceMethod

I have a need to intercept Jersey resource calls and run code before/after each ResourceMethod call. I have a ModelProcessor and am able to intercept the calls:
for(Resource resource: resourceModel.getResources()) {
for (ResourceMethod resourceMethod : resource.getResourceMethods()) {
Resource.Builder resourceBuilder = Resource.builder(...);
resourceBuilder
.addMethod(resourceMethod)
.handledBy(new Inflector<ContainerRequestContext, Response>() {
#Override
public Response apply(ContainerRequestContext containerRequestContext) {
...
}
});
}
}
However, I can't figure out how to call the original ResourceMethod here.
You might consider using Jersey Filters or Interceptors.
From the documentation:
https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/filters-and-interceptors.html#d0e9368
Filters and interceptors can be used on both sides, on the client and
the server side. Filters can modify inbound and outbound requests and
responses including modification of headers, entity and other
request/response parameters. Interceptors are used primarily for
modification of entity input and output streams. You can use
interceptors for example to zip and unzip output and input entity
streams.
Interceptors share a common API for the server and the client side.
Whereas filters are primarily intended to manipulate request and
response parameters like HTTP headers, URIs and/or HTTP methods,
interceptors are intended to manipulate entities, via manipulating
entity input/output streams. If you for example need to encode entity
body of a client request then you could implement an interceptor to do
the work for you.
So I think you want to use Interceptor because they will wrap the resource call and you will have access in the same method both before the resource call and after the resource call.
You might also go with Filters, but you will have to break your code to RequestFilter and ResponseFilter.

Does an autowired FluentProducerTemplate need to have its headers and body cleared before use within same service?

Autowired FluentProducerTemplate in a service bean will intermittently have a header set from a previous call in another method in the same service bean. I set the CamelOlingo4.keyPredicate is the header in this case.
FluentProducerTemplate producerTemplate;
UserAccount account = producerTemplate
.withHeader("CamelOlingo4.$select", "cva_useraccountid,statuscode,emailaddress,cva_firstname,cva_lastname,cva_password,cva_lastlogout,cva_lastlogin,cva_lastloginattempt,cva_lockeduntil,cva_loginattemptcount,_cva_contact_value")
.withHeader("email",username)
.withHeader("activeStatus",MSDynamicsAccountStatusCodes.ACTIVE.toString())
.withHeader("lockedStatus",MSDynamicsAccountStatusCodes.LOCKED.toString())
.to("direct:login")
.request(UserAccount.class);
And my route definition:
from("direct:login")
.id("routes.id.login")
.toD("olingo4://read/{{route.login}}?$filter=emailaddress eq '${header.email}' and (statuscode eq ${header.activeStatus} or statuscode eq ${header.lockedStatus})").log("Response from Olingo: ${body}")
.process(new OlingoProcessor());
I do fire an async request route with the keyPredicate upon successful login...
producerTemplate
.withHeader("CamelOlingo4.keyPredicate", userId)
.withHeader("Content-Type", "application/json")
.withBody(user)
.to("direct:tracklogin")
.asyncSend();
And route defined for track:login:
from("direct:tracklogin")
.id("routes.id.track.login")
.marshal()
.json(JsonLibrary.Jackson)
.convertBodyTo(String.class)
.log("JSON Body: ${id} ${body}")
.to("olingo4://patch/{{route.patch.track-login}}");
Random times, the "direct:login" route will have the keyPredicate set in the header, causing an error in my OlingoProcessor, since I'mn not getting the expected object back from the exchange body. (Olingo Object is different when querying with a keyPredicate)
Not sure if the issue lies with my implementation, the camel-olingo4 comp or the FluentProducerTemplate itself... But I do see there is a clearAll() method on the FluentProducerTemplate. I'm suspecting i need to call it whenever i use the autowired producer template within the same service bean. Just need some confirmation...
As Spring default scope is singleton, indeed the injected producer template bean instance will be reused, and the clearAll() should be called before setting headers, body, etc...
Of course, another possible solution would be to create each time a brand new producer template instance:
FluentProducerTemplate producerTemplate = context.createFluentProducerTemplate();
UserAccount account = producerTemplate.withHeader(...)
TL;DR
Does an autowired FluentProducerTemplate need to have its headers and body cleared before use within same service?
In Camel 2, yes. In Camel 3, no.
Long Story
The CamelAutoConfiguration from Camel's Spring Boot Starter create a FluentProducerTemplate with no specific scope, hence Spring's default scope singleton is applied and you have only one instance per application.
The FluentProducerTemplate is thread-safe. In Camel 2, thread-safety is achieved by storing the message body and headers in ThreadLocals. If you don't clear the headers after/before usage you may occasionally see left-overs from previous invocations. So, yes, you'd have to call clearHeader() or clearAll() manually.
The situation in Camel 3 is a bit more relaxed. There, thread-safety is achieved by producing new instances of FluentProducerTemplate whenever you call a method that would otherwise modify the template (headers, body). Clearing the values is not strictly necessary anymore as far as I can tell.

How to mock multiple responses for same request using spring's MockRestServiceServer?

Im using MockRestServiceServer for mocking http responses. In a specific scenario i call an endpoint two times and want a different response the second time.
But when i write a second expectation it's like it overwrites my first expectation.
How does one write multiple responses for the same request?
I've found it after some research:
When instantiating a MockRestServiceServer it default gets an UnorderedRequestExpectationManager. Changing this via the Builder in a SimpleRequestExpectationManager adds support for adding multiple responses in the order of defining them.
private MockRestServiceServer createMockServerBy(Class<? extends
RestTemplate> requiredType) {
RestTemplate template = context.getBean(requiredType);
return MockRestServiceServer.bindTo(template).build(new
SimpleRequestExpectationManager());
}
Have you tried WireMock? It's amazing and provides many features to mock APIs. Have a look at http://wiremock.org/

How to implement request timeout management in an AOP way in Spring Boot

currently I'm exploring approaches to implement 'request timeout management in an AOP way in Spring Boot' with several restrictions. The requirements/restrictions are stated as below:
The original purpose is that if the processing time of an api request exceeds 5 seconds, then directly return timeout result instead of continue processing
The rest api to be monitored is implemented by standard spring mvc rest controller. All apis inside are returning json strings like this:
#RestController
public class xxxxxx {
#RequestMapping(value = "xxxxxxx")
public String xxxxxx(#RequestParam(value = "xxxx", required = true) String xxxx) {
....
return json.toString();
}
}
The timeout logic is required to be implemented by AOP
(The real mean part)
No changes should be made to the controllers, which means: Request generation approach should not be changed; Return type should not be changed(No 'Callable<...>' allowed)
I have already found 1 answer(Async approach) which can perfectly resolve the problem itself with spring async, and the timeout return result is very pretty, but it's changing the return type, and also touching the code in controller. I also found one solution(AOP approach) which is using AOP, but the scenario is quite different from mine. It's already moving some business logic into AOP class, but I'm not allowed to touch the controller code. I would be grateful if anyone can provide a solution. Solutions that can't meet all the restrictions but are minimizing the differences are also admitted.
Since there is still no response to this question, I will put my own temporary solution here.
I'm using Hystrix dependency.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
It's well integrated with springboot, so the configuration is easy. Once properly configured, need to append an annotation on the request method that requires timeout handling. e.g.
#HystrixCommand(fallbackMethod="fallback")
#RequestMapping(value = "xxxxxxx")
public String xxxxxx(#RequestParam(value = "xxxx", required = true) String xxxx) {
....
return json.toString();
}
And need to add a fallback method with the name mapped to the value of 'fallbackMethod' inside annotation:
public String fallback() {
...
}
The timeout time value can be globally configured inside application.properties
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=3000
This is still not concise in these points:
Need to copy/paste this annotation for every method
Need to copy/paste the fallback method in every place hystrix is used
For hystrix fallback method itself, the parameter type and number need to be exactly same with the hystrix marked method. Currently I'm using several overloading method called 'fallback' for this, in each controller
But at least it's not changing method return types and code inside methods anymore and is the best solution I can think of currently. Will perform update once I find better solutions.

Resources