Spring Rest Controller first send 201 then do service operations - spring

Client sends a post request to gather id for asking status of operations
Spring Rest Controller class handles the post request and sends 201
created with the id
Spring Rest Controller class calls service class for starting operations
with that id
Problem: I can not return the id before starting the operations.thus client receives some information over websocket before the id is returned
#RequestMapping(value = "/operations", method = RequestMethod.POST)
public ResponseEntity<?> createNewSubscription{
String id =1;
return new ResponseEntity<>(id, HttpStatus.CREATED);
service.doOperations(id)
}
I can solve the problem by creating a thread which calls my service classes and wait 100ms in the thread and when the thread is running send 201 created to client. But it is not a good solution. Maybe I can use Spring Async rest request but it is also waiting for a callback method..How can solve that problem?

You can have the response object injected into the controller method, set the status and flush it.
public void createNewSubscription(HttpServletResponse resp) throws IOException {
resp.setStatus(HttpServletResponse.SC_CREATED);
resp.flush();
// ...
}
Flush sends the response code and headers to the client already. You can do your computation then either in the controller thread or asynchronously, eg. using Spring Async.
NB - with Spring Async your method annotated with #Async can return void which means it's "fire and forget" - you don't have to wait for it to complete.

Related

How to intercept a RequestBody before RestController and do some business rules handling from another microservice?

Basically, we have a big monolithic application built on Spring Boot v1.2 and we would like to handle some business rules processing from a MicroService (let's call it BR engine) built on Spring Boot v2.1.6.
How can I intercept the requestBody and send it first to BR engine and then once done, it will either proceed to the actual handler (Monolithic Controller) or not based from BR engine results - for simplicity let's say BR engine returns either true or false. If true, proceed to actual handler if false, return an exception.
I wanted to use HandlerInterceptorAdapter however, not sure how I can intercept the requestBody and pass it to a microservice - then from the results it will either proceed or not to the actual handler.
As an example, let's say I have a POST mapping to the Monolithic controller:
#PostMapping("/save")
public ResponseEntity<Client> save(#RequestBody ClientDTO dto) {
log.debug("Saving...");
Client newClient = Client.builder().build();
BeanUtils.copyProperties(dto, newClient);
return new ResponseEntity<>(clientService.save(newClient), HttpStatus.OK);
}
Now I wanted to intercept the ClientDTO requestBody and send it first to the BR engine and do some stuff from there. I have thought of using an interceptor and add it to my config which implements WebMvcConfigurer. However, I am not sure how can perform a restTemplate here and get a response from BR engine of pass or fail - if fail the actual handler will be skipped and just throw an exception
#Component
public class RuleEngineInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// intercept requestBody then send it to BR engine
// but how? and how can I get the response from BR engine
// to decide whether it will proceed to actual handler
// or not - probably return an exception.
return true;
}

issue with Spring and asynchronous controller + HandlerInterceptor + IE/Edge

I am working on a Spring application that serves up REST endpoints. One of the endpoints essentially acts as a proxy between the HTML client and a third party cloud storage provider. This endpoint retrieves files from the storage provider and proxies them back to the client. Something like the following (note there is a synchronous and asynchronous version of the same endpoint):
#Controller
public class CloudStorageController {
...
#RequestMapping(value = "/fetch-image/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<byte[]> fetchImageSynchronous(#PathVariable final Long id) {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
}
#RequestMapping(value = "/fetch-image-async/{id}", method = RequestMethod.GET, produces = MediaType.IMAGE_JPEG_VALUE)
public Callable<ResponseEntity<byte[]>> fetchImageAsynchronous(#PathVariable final Long id) {
return () -> {
final byte[] imageFileContents = this.fetchImage(id);
return ResponseEntity.ok().body(imageFileContents);
};
}
private byte[] fetchImage(final long id) {
// fetch the file from cloud storage and return as byte array
...
}
...
}
Due to the nature of the client app (HTML5 + ajax) and how this endpoint is used, user authentication is supplied to this endpoint differently that the other endpoints. To handle this, a HandlerInterceptor was developed to deal with authentication for this endpoint:
#Component("cloudStorageAuthenticationInterceptor")
public class CloudStorageAuthenticationInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response, final Object handler) {
// examine the request for the authentication information and verify it
final Authentication authenticated = ...
if (authenticated == null) {
try {
pResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED);
} catch (IOException e) {
throw new RuntimeException(e);
}
return false;
}
else {
try {
request.login(authenticated.getName(), (String) authenticated.getCredentials());
} catch (final ServletException e) {
throw new BadCredentialsException("Bad credentials");
}
}
return true;
}
}
The interceptor is registered like this:
#Configuration
#EnableWebMvc
public class ApiConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("cloudStorageAuthenticationInterceptor")
private HandlerInterceptor cloudStorageAuthenticationInterceptor;
#Override
public void addInterceptors(final InterceptorRegistry registry) {
registry.addInterceptor(this.cloudStorageAuthenticationInterceptor)
.addPathPatterns(
"/fetch-image/**",
"/fetch-image-async/**"
);
}
#Override
public void configureAsyncSupport(final AsyncSupportConfigurer configurer) {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(this.asyncThreadPoolCoreSize);
executor.setMaxPoolSize(this.asyncThreadPoolMaxSize);
executor.setQueueCapacity(this.asyncThreadPoolQueueCapacity);
executor.setThreadNamePrefix(this.asyncThreadPoolPrefix);
executor.initialize();
configurer.setTaskExecutor(executor);
super.configureAsyncSupport(configurer);
}
}
Ideally, the image fetching would be done asynchronously (using the /fetch-image-asyc/{id} endpoint) because it has to call a third party web service which could have some latency.
The synchronous endpoint (/fetch-image/{id}) works correctly for all browsers. However, if using the asynchronous endpoint (/fetch-image-async/{id}), Chrome and Firefox work as expect.
However, if the client is Microsoft IE or Microsoft Edge, we seem some strange behavior. The endpoint is called correctly and the response sent successfully (at least from the server's viewpoint). However, it seems that the browser is waiting for something additional. In the IE/Edge DevTools window, the network request for the image shows as pending for 30 seconds, then seems to timeout, updates to successful and the image is successfully display. It also seems the connection to the server is still open, as the server side resources like database connections are not released. In the other browsers, the async response is received and processed in a second or less.
If I remove the HandlerInterceptor and just hard-wire some credentials for debugging, the behavior goes away. So this seems to have something to with the interaction between the HandlerInterceptor and the asynchronous controller method, and is only exhibited for some clients.
Anyone have a suggestion on why the semantics of IE/Edge are causing this behavior?
Based on your description, there are some different behaviors when using IE or Edge
it seems that the browser is waiting for something additional
the connection seems still open
it works fine if remove HandlerInterceptor and use hard code in auth logic
For the first behavior, I would suggest you use fiddler to trace all http requests. It is better if you could compare two different actions via fiddler (1) run on chrome, 2) run on edge ). Check all http headers in requests and responses carefully to see whether there is some different part. For the other behaviors, I would suggest you write logs to find which part spend the most time. It will provide you useful information to troubleshot.
After much tracing on the server and reading through the JavaDocs comments for AsyncHandlerInterceptor, I was able to resolve the issue. For requests to asynchronous controller methods, the preHandle method of any interceptor is called twice. It is called before the request is handed off to the servlet handling the request and again after the servlet has handled the request. In my case, the interceptor was attempting to authenticate the request for both scenarios (pre and post request handling). The application's authentication provider checks credentials in a database. For some reason if the client is IE or Edge, the authentication provider was unable to get a database connection when called from preHandle in the interceptor after the servlet handled the request. The following exception would be thrown:
ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.dao.DataAccessResourceFailureException: Could not open connection; nested exception is org.hibernate.exception.JDBCConnectionException: Could not open connection] with root cause
java.sql.SQLTransientConnectionException: HikariPool-0 - Connection is not available, request timed out after 30001ms.
So the servlet would successfully handle the request and send a response, but the filter would get hung up for 30 seconds waiting for the database connection to timeout on the post processing called to preHandle.
So for me, the simple solution was to add a check in preHandle if it is being called after the servlet has already handled the request. I updated the preHandle method as follows:
#Override
public boolean preHandle(final HttpServletRequest pRequest, final HttpServletResponse pResponse, final Object pHandler) {
if (pRequest.getDispatcherType().equals(DispatcherType.REQUEST)) {
... perform authentication ...
}
return true;
}
That solved the issue for me. It doesn't explain everything (i.e., why only IE/Edge would cause the issue), but it seems that preHandle should only do work before the servlet handles the request anyways.

Diffrence b/w #ResponseStatus and ResponseEntity.created(location).build() in post method of rest controller in spring boot

For a POST method in Rest controller I want to return status code 201.
I saw two approaches for that.
First one is:
#PostMapping("/offers")
#ResponseStatus(HttpStatus.CREATED)
public Offer createOffer(#Valid #RequestBody Offer offer) {
return offerRepository.Save(offer);
}
Second approach is:
#PostMapping("/offers")
public ResponseEntity<Object> createOffer(#Valid #RequestBody Offer offer) {
return offerService.createOffer(offer);
}
Below is my service class:
#Override
public ResponseEntity<Object> createOffer(Offer offer) {
Offer uOffer=OfferRepository.save(offer);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(uOffer.getJobTitle()).toUri();
return ResponseEntity.created(location).build();
}
So my question is: for first approach we are not using any ResponseEntity.created as we are simply returning #ResponseStatus(HttpStatus.CREATED) from controller. But in the second we are not using #ResponseStatus(HttpStatus.CREATED) and we are handling that status code 201 by using uri and response entity.
What is the difference b/w the both approaches? Both seems to be same as they are returning the same response code 201. which one is preferred?
In my opinion you should apply the following rules. If you want to return a ResponseEntity then use that to affect the status. Thus something like:
#PostMapping("/offers")
public ResponseEntity<Offer> createOffer(#Valid #RequestBody Offer offer) {
Offer offer = offerService.createOffer(offer);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{jobTitle}").
buildAndExpand(uOffer.getJobTitle()).toUri();
return ResponseEntity.created(location)
.body(offer)
.build();
}
Do not allow your service to generate the ResponseEntity as this is a view class for controllers and should not be in a service.
The second option is by using the class rather then response entity. Then the example would be something like:
#PostMapping("/offers")
#ResponseStatus(HttpStatus.CREATED)
public Offer createOffer(#Valid #RequestBody Offer offer) {
// Do not return response entity but the offer
return offerService.createOffer(offer);
}
There is no difference in general when it comes to status codes. In the end, you still get an HTTP response with 201 status code.
However, in second approach you're also returning a Location header which is a preferred way to do. From Mozilla's HTTP guide:
The HTTP 201 Created success status response code indicates that the request has succeeded and has led to the creation of a resource. The new resource is effectively created before this response is sent back and the new resource is returned in the body of the message, its location being either the URL of the request, or the content of the Location header.
The first approach is the preferred one, since it allows you to keep your service layer decoupled from your web layer (service layer should not know about HttpEntity and all that stuff, so it could potentially be reused without the web layer).
You should refactor you service method to return Object instead of ResponseEntity<Object>.
What is the difference b/w the both approaches?
Using return ResponseEntity.created(location).build(); adds the Location header to the response.
Is also recommended to return the new resource in the body of the response.
201 Created
The HTTP 201 Created success status response code indicates that the request has succeeded and has led to the creation of a resource. The new resource is effectively created before this response is sent back and the new resource is returned in the body of the message, its location being either the URL of the request, or the content of the Location header.
Thus the best option would be:
ResponseEntity.created(location).body(uOffer);

Call back rest url using spring framework

I writing rest web services using spring framework 4.0 jars.
All the get calls and post are working fine.
I wanted to know how to implement an api which works asynchronously.
The client should post to this url and wait for the response ; something like call back mechanism between server and client . Server when gets the data posts response to this url.
Thanks in advance
Spring 4.0 framework has an easy solution to this.
Changed the Return type of the method DeferredResult .
Create an instance of DeferredResult and assign whenever value is assigned to this instance it would return the response to calling request.
#RequestMapping(method=RequestMethod.GET)
public DeferredResult<String> getNewMessage(HttpServletRequest request, HttpServletResponse response)
{
DeferredResult<String> deferredResult = new DeferredResult<String>();
processRequest(deferredResult);
return deferredResult;
}
void processRequest( DeferredResult<String> result)
{
result.setResult("hello world");
}

Request asynchronous processing does not work

I would like to make my controller (Spring MVC) handle request in parallel. I use #Callable below and it does not work, because next request is handled since first one finished (returns view).
#RequestMapping(method = RequestMethod.GET)
public Callable<String> helloWorld(final Model model) throws InterruptedException {
return new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(5000);
return "helloWorld";
}
};
}
Do I need any special code?
The documentation for Spring MVC states the following about the Callable<?> return type for handler methods
A Callable can be returned when the application wants to produce
the return value asynchronously in a thread managed by Spring MVC.
Spring will take the returned Callable instance, pass it to an ExecutorService (actually does more processing then than that, but for simplicity) and execute it in a different Thread then your initial request. After waiting 5 seconds, it will return the view associated with the view name "helloWorld".

Resources