Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring REST service: retrieving JSON from Request

Tags:

I am building a REST service on Spring 3.1. I am using @EnableWebMVC annotation for that. Since my service will only be accepting JSON requests, I would also like to dump the incoming request into a MongoDB collection for logging (and, later, for data transformation). I would like to access the raw JSON Request (which I could do on a non-spring implementation using "@Content HttpServletRequest request" as a method parameter).

I am a Spring newbie. So, kindly help me with directions to achieve this. Thanks!

UPDATE: The issue is not completely resolved. Only my tests with GET worked. It fails with POST. Therefore unchecked the accepted answer

The issue is, even if I create a HttpServletRequestWrapper, I cannot forward the request after I process and wrap the request. Here is what happens:

Interceptor:

public class DBLogInterceptor extends HandlerInterceptorAdapter {     MyRequestWrapper requestWrapper;      private final static Logger logger = Logger.getLogger(DBLogInterceptor.class);      @Override     public boolean preHandle(             HttpServletRequest request,             HttpServletResponse response,             Object handler) throws Exception      {         requestWrapper = new MyRequestWrapper(request);         // Code removed, but it just dumps requestWrapper.getBody() into DB         return super.preHandle(requestWrapper, response, handler);     } } 

HTTP POST Servicing method

@RequestMapping(method = RequestMethod.POST, consumes="application/json", produces="application/json", value = "employee") @ResponseBody public String updateEntity(@RequestBody Employee emp) {     // Do some DB Stuff. Anyway, the control flow does not reach this place.     return "Employee " + emp.getName() + " updated successfully!"; } 

Now I get an exception whenever I send a POST:

12:04:53,821 DEBUG DBLogInterceptor:22 - {"name":"Van Damme","dept":"Applied Martial Arts"} 12:04:53,843 DEBUG RequestResponseBodyMethodProcessor:117 - Reading [com.test.webapp.login.domain.Employee] as "application/json" using [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter@154174f9] 12:04:53,850 DEBUG ExceptionHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Stream closed 12:04:53,854 DEBUG ResponseStatusExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Streamclosed 12:04:53,854 DEBUG DefaultHandlerExceptionResolver:132 - Resolving exception from handler [public java.lang.String com.test.webapp.controller.EmployeeService.updateEntity(com.test.webapp.login.domain.Employee)]: java.io.IOException: Streamclosed 12:04:53,859 DEBUG DispatcherServlet:910 - Could not complete request java.io.IOException: Stream closed         at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:312)         at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:200)         at org.codehaus.jackson.impl.ByteSourceBootstrapper.ensureLoaded(ByteSourceBootstrapper.java:507)         at org.codehaus.jackson.impl.ByteSourceBootstrapper.detectEncoding(ByteSourceBootstrapper.java:129)         at org.codehaus.jackson.impl.ByteSourceBootstrapper.constructParser(ByteSourceBootstrapper.java:224)         at org.codehaus.jackson.JsonFactory._createJsonParser(JsonFactory.java:785)         at org.codehaus.jackson.JsonFactory.createJsonParser(JsonFactory.java:561)         at org.codehaus.jackson.map.ObjectMapper.readValue(ObjectMapper.java:1914)         at org.springframework.http.converter.json.MappingJacksonHttpMessageConverter.readInternal(MappingJacksonHttpMessageConverter.java:124)         at org.springframework.http.converter.AbstractHttpMessageConverter.read(AbstractHttpMessageConverter.java:153)         at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:120)         at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodArgumentResolver.readWithMessageConverters(AbstractMessageConverterMethodArgumentResolver.java:91)         at org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.resolveArgument(RequestResponseBodyMethodProcessor.java:71)         at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:75)         at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:156)         at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)         at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)         at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)         at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)         at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)         at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)         at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)         at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)         at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)         at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)         at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)         at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)         at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:225)         at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)         at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)         at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)         at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)         at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:927)         at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)         at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407)         at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:999)         at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)         at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:307)         at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(Unknown Source)         at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)         at java.lang.Thread.run(Unknown Source) 

I expected the HttpServletRequestWrapper to be taking care of caching the request. But it doesn't happen somehow.

like image 434
user1323865 Avatar asked May 05 '12 01:05

user1323865


People also ask

How consume JSON from RESTful web service in spring boot?

The class App implements CommandLineRunner and calls the SpringApplication. run() method by passing this instance of class App. class. This will, in turn, call the run method, where we have code to call a RESTful Web Service and consume JSON response using RestTemplate class of Spring framework.

Can we send JSON object in GET request in REST API?

To get JSON from a REST API endpoint, you must send an HTTP GET request and pass the "Accept: application/json" request header to the server, which will tell the server that the client expects JSON in response.

Can we pass JSON payload GET request?

To answer your question, yes you may pass JSON in the URI as part of a GET request (provided you URL-encode).

How pass JSON object in post request in spring boot?

Send JSON Data in POST Spring provides a straightforward way to send JSON data via POST requests. The built-in @RequestBody annotation can automatically deserialize the JSON data encapsulated in the request body into a particular model object. In general, we don't have to parse the request body ourselves.


2 Answers

Using the HttpServletRequest object, you can get access to the URL the client used to make the request, the method used (GET, POST, PUT, etc), the query string, and headers.

Getting the RequestBody may be a bit trickier and may require using the HttpServletRequestWrapper object. Since the request body can only be read once, you'll need to extend the wrapper to access it so that your target controller can still access it later to deserialize your JSON into POJO objects.

public class MyRequestWrapper extends HttpServletRequestWrapper {  private final String body;  public MyRequestWrapper(HttpServletRequest request) throws IOException {    super(request);    StringBuilder stringBuilder = new StringBuilder();    BufferedReader bufferedReader = null;    try {      InputStream inputStream = request.getInputStream();      if (inputStream != null) {        bufferedReader = new BufferedReader(new InputStreamReader(inputStream));        char[] charBuffer = new char[128];        int bytesRead = -1;        while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {          stringBuilder.append(charBuffer, 0, bytesRead);        }      } else {        stringBuilder.append("");      }    } catch (IOException ex) {        throw ex;    } finally {      if (bufferedReader != null) {        try {          bufferedReader.close();        } catch (IOException ex) {          throw ex;        }      }    }    body = stringBuilder.toString();  }   @Override  public ServletInputStream getInputStream() throws IOException {    final ByteArrayInputStream byteArrayInputStream = new     ByteArrayInputStream(body.getBytes());    ServletInputStream servletInputStream = new ServletInputStream() {      public int read() throws IOException {        return byteArrayInputStream.read();      }    };    return servletInputStream;  }   @Override  public BufferedReader getReader() throws IOException {    return new BufferedReader(new InputStreamReader(this.getInputStream()));  }   public String getBody() {    return this.body;  } } 

To access the requests in a central location, you can use either a Filter or a Spring Interceptor. Both of these are invoked prior to the request being delegated to the controller, and both have access to the servlet.

Here is an actual Logging example using a Spring Interceptor:

package com.vaannila.interceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.BasicConfigurator; import org.apache.log4j.Logger; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.handler. HandlerInterceptorAdapter;  public class LoggerInterceptor extends HandlerInterceptorAdapter {     static Logger logger = Logger.getLogger(LoggerInterceptor.class);      static {         BasicConfigurator.configure();     }      @Override      public boolean preHandle(HttpServletRequest request,         HttpServletResponse response, Object handler) throws Exception {          logger.info("Before handling the request");         return super.preHandle(request, response, handler);     }      @Override     public void postHandle(HttpServletRequest request,         HttpServletResponse response, Object handler,             ModelAndView modelAndView) throws Exception {          logger.info("After handling the request");         super.postHandle(request, response, handler, modelAndView);     }      @Override     public void afterCompletion(HttpServletRequest request,         HttpServletResponse response, Object handler, Exception ex)             throws Exception {          logger.info("After rendering the view");         super.afterCompletion(request, response, handler, ex);     } }   <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     xmlns:p="http://www.springframework.org/schema/p"     xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans.xsd">      <bean id="viewResolver" class="org.springframework.web.servlet.view.    InternalResourceViewResolver" p:prefix="/WEB-INF/jsp/" p:suffix=".jsp" />      <bean id="handlerMapping" class="org.springframework.web.servlet.handler. BeanNameUrlHandlerMapping" p:interceptors-ref="loggerInterceptor" />      <bean id="loggerInterceptor" class="com.vaannila.interceptor.LoggerInterceptor" />      <bean id="userService" class="com.vaannila.service.UserServiceImpl" />      <bean name="/userRegistration.htm" class="com.vaannila.web.UserController" p:userService-ref="userService" p:formView="userForm" p:successView="userSuccess" />  </beans> 

In the LoggerInterceptor, you could use the following code to access the request:

MyRequestWrapper myRequestWrapper = new MyRequestWrapper((HttpServletRequest) request);  String body = myRequestWrapper.getBody(); String clientIP = myRequestWrapper.getRemoteHost(); int clientPort = request.getRemotePort(); String uri = myRequestWrapper.getRequestURI();  System.out.println(body); System.out.println(clientIP); System.out.println(clientPort); System.out.println(uri); 
like image 158
jmort253 Avatar answered Oct 03 '22 20:10

jmort253


I doubt if HttpServletRequestWrapper can ever work... Take a look at the DispatcherServlet implementation:

            HandlerInterceptor[] interceptors = mappedHandler.getInterceptors();             if (interceptors != null) {                 for (int i = 0; i < interceptors.length; i++) {                     HandlerInterceptor interceptor = interceptors[i];                     if (!interceptor.preHandle(processedRequest, response, mappedHandler.getHandler())) {                         triggerAfterCompletion(mappedHandler, interceptorIndex, processedRequest, response, null);                         return;                     }                     interceptorIndex = i;                 }             }              // Actually invoke the handler.             mv = ha.handle(processedRequest, response, mappedHandler.getHandler()); 

It passes reference to "processedRequest" still, which refers to a HttpServletRequest request whose stream has already been read.

like image 44
user1323865 Avatar answered Oct 03 '22 18:10

user1323865