如何在spring mvc中的操作之前发送响应

Posted

技术标签:

【中文标题】如何在spring mvc中的操作之前发送响应【英文标题】:How to send response before actions in spring mvc 【发布时间】:2015-11-11 09:08:28 【问题描述】:

说我的spring控制器函数接收到大量数据。 鉴于数据结构正确,我想返回 200 OK,并且在那之后我想执行处理,这可能需要一段时间。

据我了解,发送响应的唯一方法是通过 return 命令。但我不想在响应发送时结束该功能。

还有其他方法可以在函数中间向客户端发送响应吗?

创建一个新的线程运行是显而易见的,但其他语言 (JS) 可以让您更优雅地处理它。

@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??

【问题讨论】:

使用@Async 方法可以包装你想运行的所有东西,它将被不同的线程处理 This might help 【参考方案1】:

你可以使用@Async

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



@Async 
void do()
   //your code

这项工作在 java 8 中

【讨论】:

【参考方案2】:

您应该使用HandlerInterceptor。但是代码比预期的要复杂一些。因此,这里有一个代码建议,通过将整个解决方案放在一个类中来使其更简单:

@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.
        
    ;

【讨论】:

【参考方案3】:

您当然可以在发送响应后进行处理。更通用的方法是使用HandlerInterceptorafterCompletion 方法。通过构造,它将在响应发送到客户端后执行,但它会强制您将逻辑​​拆分为 2 个组件,即控制器中的 before 部分和 after参与拦截器。

另一种方法是忘记 Spring MVC 机制并在控制器中手动提交响应:

@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
    ...

注意 void 返回代码以通知 Spring 响应已在控制器中提交。

作为一个附带优势,处理仍然发生在同一个线程中,因此您可以完全访问会话范围的属性或 Spring MVC 或 Spring Security 使用的任何其他线程局部变量...

【讨论】:

如果请求来自两个不同的用户,那么它会共享相同的变量还是为不同的用户共享不同的变量?? 你用过变量 res,那个变量指的是什么? 我不知道。它在原始问题中,所以我将其保留在我的答案中。我只是假设这是一条用于提供响应的消息。 嗨@SergeBallesta,我尝试了您的解决方案,但没有奏效。在 wr.close() 之后,我将 sleep 方法放置了 10 秒。当我点击请求时,它会等待 10 秒发送回响应。 PFB 代码。输出流.close(); System.out.println("流关闭。"); System.out.println("睡觉");尝试 Thread.sleep(10000); catch (InterruptedException e) e.printStackTrace(); System.out.println("唤醒"); @SergeBallesta,如果我遗漏了什么,请帮忙。【参考方案4】:

我猜你mau使用spring的异步机制 servlet 3.0 中引入了异步方法,Spring 为它们提供了一些支持 基本上……你提出请求;请求由服务器处理,然后在后台,一个新线程管理请求数据 这是一个有用的链接(至少我希望:))http://spring.io/blog/2012/05/10/spring-mvc-3-2-preview-making-a-controller-method-asynchronous/

【讨论】:

以上是关于如何在spring mvc中的操作之前发送响应的主要内容,如果未能解决你的问题,请参考以下文章

Spring mvc jackson json mapper-如何将unicode编码应用于json响应中的特殊字符

如何在 Spring 中缩小动态 HTML 响应?

在 Spring MVC 中以 AJAX 响应发送文件

如何在 Spring MVC 中代理 HTTP 请求?

如何在 Spring MVC 中为错误响应启用 CORS?

Spring-MVC:如何从控制器流式传输 mp3 文件