简体   繁体   English

如何将@RequestBody 值传递给转发控制器 Spring MVC

[英]How to pass @RequestBody value to forwarded controller Spring MVC

I have to consume value of @RequestBody to forwarded controller.我必须将@RequestBody值消耗到转发控制器。 As I passed JSON as a request body and from one controller it will forward to another controller.当我将 JSON 作为请求主体传递时,它将从一个控制器转发到另一个控制器。 But it second controller it gives java.io.IOException但是它的第二个控制器给出了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.所以有什么办法可以用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.您收到异常是因为@RequestBody注释读取底层 servlet 输入流并使用它,因此您无法读取它两次。

If you look closer at the stack trace you can note the origin如果您仔细查看堆栈跟踪,您可以注意到原点

org.apache.catalina.connector.InputBuffer.readByte(InputBuffer.java:339) 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.如果您可以将 json 放在 http 调用(授权)的参数中,您可以避免这个问题,但是(授权)它不再是 REST ednpoint,而是类似于“简单”http 集成的东西。

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.这只有在/myapp/second//myapp/secondError/是真实 URL /myapp/secondError/ ,如果不是,并且如果方法loginloginError应该只从authorize方法调用,它们应该只是控制器中的私有方法。

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:我想构建一个接收 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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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