【问题标题】:How to pass @RequestBody value to forwarded controller Spring MVC如何将@RequestBody 值传递给转发控制器Spring MVC
【发布时间】:2015-04-30 10:58:31
【问题描述】:

我必须将@RequestBody 的值消耗给转发控制器。当我将 JSON 作为请求主体传递时,它会从一个控制器转发到另一个控制器。但它是第二个控制器,它给了java.io.IOException

MyController.java

@Controller
public class MyController {

/* 
Request JSON like  
{"personeId":"123789","personName":"Fitz"}
*/
@RequestMapping(value = "/myapp/first/", method = RequestMethod.POST, 
        consumes = { "application/json" })
public String authorize(@RequestBody Person person) {
    //Looking good
    if(Validator.validatePerson(perosn)) {
        return "forward:/myapp/second/";
    } else {
        return "forward:/myapp/secondError/";
    }
}

@RequestMapping(value = "/myapp/second/", method = RequestMethod.POST, 
        consumes = { "application/json" })
public @ResponseBody String login(@RequestBody Person person) {
    // Not able to get Person object here.
    // Getting java.io.IOException: Stream closed :( 
    System.out.println("---> "+person.getPersonName());
    return "success";
}

@RequestMapping(value = "/myapp/secondError/", method = RequestMethod.POST,
        consumes = { "application/json" })
public @ResponseBody String loginError() {
    return "error";
}
}

堆栈跟踪

org.apache.catalina.core.ApplicationDispatcher invoke
SEVERE: Servlet.service() for servlet dispatcher threw exception
java.io.IOException: Stream closed
    at org.apache.catalina.connector.InputBuffer.readByte(InputBuffer.java:339)
    at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:94)
    at java.io.FilterInputStream.read(FilterInputStream.java:83)
    at java.io.PushbackInputStream.read(PushbackInputStream.java:139)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.readWithMessageConverters(RequestResponseBodyMethodProcessor.java:168)
    at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:105)
    at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:77)
    at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:162)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:129)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:777)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:706)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:721)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:466)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:391)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:318)
    at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:168)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1228)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1011)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:955)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:868)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:644)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:301)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:503)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:136)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:74)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1015)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:652)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1575)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1533)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

所以有什么办法可以用 Spring MVC 实现这个场景。

【问题讨论】:

  • 尝试在authorize方法中将person对象放到modelMap中,在login方法中访问。
  • 为什么要转发到另一个控制器方法?要么只使用一种控制器方法,要么通过将数据作为参数传递来调用第二种方法。
  • @user1354678 正如你所提到的,我将使用调用方法,或者我可以将所需的对象放入会话中。谢谢。
  • 我建议通过传递参数调用方法来做到这一点。不要在会话中添加它,除非它必须存储在会话中。

标签: java json spring spring-mvc


【解决方案1】:

您收到异常是因为 @RequestBody 注释读取底层 servlet 输入流并使用它,因此您无法读取两次。

如果您仔细查看堆栈跟踪,您可以注意到来源

org.apache.catalina.connector.InputBuffer.readByte(InputBuffer.java:339)

您正在尝试(重新)从已关闭的流中读取数据。

如果您想保留第一个控制器,您必须更改第二个,例如,您可以选择重定向到第二个控制器并将对象置于会话中,或者从第二个控制器中提取一个方法来执行该业务逻辑并从第一个控制器调用它。

如果您可以将 json 放入 http 调用(授权)的参数中,则可以避免此问题,但(授权)它不再是 REST ednpoint,而是类似于“简单”http 集成。

【讨论】:

    【解决方案2】:

    在这种情况下您不应该转发,而只需调用其他方法:

    @RequestMapping(value = "/myapp/first/", method = RequestMethod.POST, 
            consumes = { "application/json" })
    public String authorize(@RequestBody Person person) {
        //Looking good
        if(Validator.validatePerson(perosn)) {
            return login(person);
        } else {
            return loginError();
        }
    }
    

    这只有在 /myapp/second//myapp/secondError/ 是真实的 URL 时才有意义,如果不是,并且如果方法 loginloginError 只能从 authorize 方法调用,它们应该只是私有方法在控制器中。

    当然,如果涉及到业务规则,应该进入服务层。

    【讨论】:

      【解决方案3】:

      我认为您的方法并不理想。我将只调用一次控制器,然后开始将不同的任务委托给不同的服务。

      if(Validator.validatePerson(perosn)) {
          return loginService.login(person);
      } else {
          return "error";
      }
      

      【讨论】:

      • 是的,你是对的,我的方法看起来并不酷。因此,根据 Serge Ballesta 的建议,我们可以调用方法。
      【解决方案4】:

      接受的答案是正确的,没有合适的解决方案。

      但是,这里有一个对我有用的解决方法。我想构建一个接收 json 并基于 command 字段在内部转发请求的调度程序:

      @Controller
      public class StackoverflowExampleController {
      
          private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
      
          @PostMapping("/post")
          public ModelAndView post(@RequestBody String body) {
              JsonNode tree = OBJECT_MAPPER.readTree(body);
              String command = tree.get("command").asText();
              return new ModelAndView("forward:/" + command + "?json=" + URLEncoder.encode(body)); 
          }
          
          @PostMapping("/test")
          @ResponseBody
          public String test(@RequestParam String json) {
              return json;
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-10-05
        • 2013-03-19
        • 1970-01-01
        • 2014-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多