简体   繁体   中英

How to pass @RequestBody value to forwarded controller Spring MVC

I have to consume value of @RequestBody to forwarded controller. As I passed JSON as a request body and from one controller it will forward to another controller. But it second controller it gives 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";
}
}

StackTrace

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)

So there is any way to achieve this scenario with Spring MVC.

you are getting the exception because @RequestBody annotation reads the underlying servlet input streams and consumes it, so you cannot read it twice.

If you look closer at the stack trace you can note the origin

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

You are trying to (re)read the data from a stream that is closed.

If you want to keep the first controller as it now you must change the second, you can choose for example to redirect to the second controller and put the object in session or to extract a method from the second controller that does the business logic and call it from the first controller.

If you can put the json in a parameter of the http call (authorize) you can avoid this problem, but (authorize) it's not a REST ednpoint anymore, but something like a "simple" http integration.

You should not forward in that case, but simply call the other methods :

@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();
    }
}

And this would only have sense if /myapp/second/ and /myapp/secondError/ are real URLs, if not, and if methods login and loginError should only be called from authorize method, they should simply be private methods in the controller.

Of course, if it involved business rules, it should go in service layer.

I don't think your approach is ideal. I would have only one call to the controller, and then start delegating the different tasks to different services.

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

The accepted answer is correct, there is no proper solution.

However here's a workaround that worked for me. I wanted to build a dispatcher that takes in json and forwards the request internally based on a command field:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM