orshachar
orshachar

Reputation: 5037

How to send response before actions in spring mvc

Say that my spring controller function receives a large amount of data. I want to return 200 OK, given that the data is structured right, and after that I want to perform the processing, which might take a while.

To my understanding the only way to send response is by return command. But I don't want to end the function on response send.

Are there other ways to send response to client at the middle of the function?

Creating a new thread run is obvious but other languages (JS) let you handle it more elegantly.

@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message> messages) {
    HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
            : HttpStatus.NOT_FOUND;
    return new ResponseEntity<String>(res, code);
   // how do I add code here??
}

Upvotes: 9

Views: 14091

Answers (4)

Javier Solis Guzman
Javier Solis Guzman

Reputation: 318

You can use @Async, only need to pass your code to other bean (you could use @Service, @Component), and inject to your controller.

This is the method of your controller

@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = 
      RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message> 
      messages) {
    otherService.do();
    HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
        : HttpStatus.NOT_FOUND;
     return new ResponseEntity<String>(res, code);

}

And this is the other bean

@Service
class OtherService{

    @Async 
    void do(){
       //your code
    }
}

Upvotes: 4

Italo Borssatto
Italo Borssatto

Reputation: 15709

You should use the HandlerInterceptor. But the code get a little bit more complex than expected. So, here's a code suggestion to make it simpler by putting the whole solution in a single class:

@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public ResponseEntity<String> doSomething(@RequestBody List<Message> messages) {
    HttpStatus code = (messages!=null && !messages.isEmpty()) ? HttpStatus.OK
            : HttpStatus.NOT_FOUND;

    result.set(res); // Save the object to be used after response

    return new ResponseEntity<String>(res, code);
}

private static final ThreadLocal<String> result = new ThreadLocal<String>();

@Bean
public HandlerInterceptor interceptor() {
    return new HandlerInterceptor() {
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
            // Get the saved object and clean for the next request
            String res = result.get();
            result.set(null);

            // TODO Your code to be executed after response.
        }
    };
}

Upvotes: 0

Serge Ballesta
Serge Ballesta

Reputation: 149185

You can of course do processing after sending the response. The more general way would be to use the afterCompletion method of a HandlerInterceptor. By construction, it will be executed after the response have been sent to client, but it forces you to split you logic in 2 components the before part in controller, and the after part in the interceptor.

The alternative way is to forget Spring MVC machinery and manually commit the response in the controller:

@RequestMapping(value = Connectors.CONNECTOR_HEARTBEAT, method = RequestMethod.POST)
public void doSomething(@RequestBody List<Message> messages, HttpServletResponse response) {
    int code = (messages!=null && !messages.isEmpty()) ? HttpServletResponse.SC_OK
            : HttpServletResponse.SC_NOT_FOUND;
    if (code != HttpServletResponse.SC_OK) {
        response.sendError(code, res);
        return;
    }
    java.io.PrintWriter wr = response.getWriter();
    response.setStatus(code);
    wr.print(res);
    wr.flush();
    wr.close();

    // Now it it time to do the long processing
    ...
}

Note the void return code to notify Spring that the response have been committed in the controller.

As a side advantage, the processing still occurs in the same thread, so you have full access to session scoped attributes or any other thread local variables used by Spring MVC or Spring Security...

Upvotes: 11

Angelo Immediata
Angelo Immediata

Reputation: 6954

I guess you mau use the async mechanism of spring Async methods have been introduced in servlet 3.0 and Spring offers some support to them Basically... you make a request; the request is handled by the server and then, in background, a new thread manages the requesta data Here a useful link (at least i hope :) ) http://spring.io/blog/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/

Upvotes: 2

Related Questions