-1

Is there any way to read inputStream of a request without losing the data in it.


I am trying to take a raw copy of my request into string before processing it. But once I read the inputstream from request, the inputstream is changing to null so, I can't get Parameter from my request later. I tried using CustomHttpServletRequestWrapper but it did not work. Below is the snippet of my code.

The InboundHandler is my handler class which has the processRequest(HttpServletRequest request, HttpServletResponse response) method that is invoked from my servlet class.

public class InboundHandler {

 protected void processRequest(HttpServletRequest request, HttpServletResponse response)
 throws ServletException, IOException {
  try {
   CustomHttpServletRequestWrapper requestWrapper = new CustomHttpServletRequestWrapper(request);
   String body = getRequestBody(requestWrapper.getInputStream());

   String from = request.getParameter("from"); //which I'm getting null here
   // I also tried using 
   // String from = requestWrapper.getParameter("from"); // Even this did not work
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 private String getRequestBody(InputStream inputStream) {

  StringBuilder stringBuilder = new StringBuilder();
  BufferedReader bufferedReader = null;
  try {
   if (inputStream != null) {
    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
    char[] charBuffer = new char[128];
    int bytesRead = -1;
    while ((bytesRead = bufferedReader.read(charBuffer)) != -1) {
     stringBuilder.append(charBuffer, 0, bytesRead);
    }
   }
  } catch (IOException ex) {
   ex.printStackTrace();
   //throw new AuthenticationException("Error reading the request payload", ex);
  } finally {
   if (bufferedReader != null) {
    try {
     bufferedReader.close();
    } catch (IOException iox) {
     // ignore
    }
   }
  }

  return stringBuilder.toString();
 }

 public class CustomHttpServletRequestWrapper extends HttpServletRequestWrapper {

  private final String body;

  public CustomHttpServletRequestWrapper(HttpServletRequest request) {
   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) {
    ex.printStackTrace();
    //log.error("Error reading the request body...");  
   } finally {
    if (bufferedReader != null) {
     try {
      bufferedReader.close();
     } catch (IOException ex) {
      ex.printStackTrace();
      //log("Error closing bufferedReader...");  
     }
    }
   }

   body = stringBuilder.toString();
  }

  @Override
  public ServletInputStream getInputStream() throws IOException {

   final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(body.getBytes());

   ServletInputStream inputStream = new ServletInputStream() {
    public int read() throws IOException {
     return byteArrayInputStream.read();
    }
   };

   return inputStream;
  }
 }
}
Dag Høidahl
  • 7,873
  • 8
  • 53
  • 66
Raj K
  • 458
  • 2
  • 7
  • 22
  • The javadoc for ServletRequest warns against reading the body and then using the getParameter method: "If the parameter data was sent in the request body, such as occurs with an HTTP POST request, then reading the body directly via getInputStream() or getReader() can interfere with the execution of this method." Input streams can be reset, but probably that is not what you want. Maybe you could populate your domain object once from the body and then pass that to the rest of your code? – Keefe Roedersheimer Jan 25 '16 at 16:57
  • Yea I read the javadoc for ServletRequest it says reading the body directly via getInputStream() can interfere with the execution of this getParameter() method. I was wondering if there is any possible way to get required parameters without populating domain object from the String body which looks not a sophisticated way. I prefer to use the getParameter rather than pulling my domain objects from a string body. @ Keefe Roedersheimer – Raj K Jan 25 '16 at 17:43
  • No I'm still trying on this @Perdomoff – Raj K Jan 25 '16 at 19:55
  • As long as you are confident these will always be parameters (so you are not posting JSON) then I think it is fine to just always use getParameter calls - it is more complicated to do multipart file upload stuff; does it work with just using getParameter and never parsing the body? – Keefe Roedersheimer Jan 25 '16 at 20:11
  • Did you try copying your inputstream to a byte array? – Perdomoff Jan 25 '16 at 20:18
  • Hi @ Keefe Roedersheimer, I'm not posting JSON, at client side i'm trying to build a multipart form which has form fields and a file. And at receiver(servlet) i'm trying to copy the raw post first for logging/backup into a String and later performing getParameters() for reverting form fields and getPart() for retrieving a file. as I'm expecting my parameters as String I'm never parsing them as my return value for getParameter() is String. – Raj K Jan 25 '16 at 20:38
  • Were you able to resolve this? – Perdomoff Jan 26 '16 at 16:28
  • @Perdomoff - No I'm still trying this, I'm tried by create a separate class that extend HttpServletRequestWrapper and read the inputstream into byte array in it's constructor, but it still did not resolve my problem, so still I'm looking on online for any relevent solution that I can use to my requirements with little modifications. – Raj K Jan 26 '16 at 18:09
  • I really appreciate your ideas to solve this. – Raj K Jan 26 '16 at 18:11

1 Answers1

-1

Q: Is there any way to read inputStream of a request without losing the data in it?

A: Yes - simply "save" what you "read"!

As Perdomoff correctly said, "Requests aren't meant to be reused". You can't put the water back in the hose once you've watered your garden :)

You can save the data. Perdomoff suggested using a session variable.

Another alternative might be to use a Servlet Filter. Here is a good tutorial:

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • yea Request can't be reused once it is consumed, and I also agree that perdomoff suggested a good solution but it doesn't suite for my suituation, I'm trying to figure out if there is any way to backup my raw request inputstream as soon as i receive it from client and later I need this request object for handling other process, I tried servlet Filter to read inputstream of a request by wrapping it using HttpServletRequestWrapper the problem here after doing this I'm receiving an request object with empty inputstream in my servlet, so I can't handle other process with empty request – Raj K Jan 27 '16 at 13:37
  • If you used a filter *correctly* you shouldn't have any problem. Maybe you have to abandon using HttpServletRequestWrapper - I don't know. But 1) follow the tutorials, 2) Create a *simple* test case with a filter, and 3) See if you can do the same with your actual application. – paulsm4 Jan 27 '16 at 18:19
  • I think I need to overide the getParameter and other methods want I need in wrapper, So I can use request more than once, first time for printing request inputstream and for later using getParameter to get required parameter on the request – Raj K Feb 12 '16 at 18:19
  • Dude - again, like correctly said last January: "Requests aren't meant to be reused". Forget the stupid wrapper idea and learn how to use a filter – paulsm4 Feb 12 '16 at 23:31
  • Thanks dude, I really appreciate your help with this question, I will just use filter. and post back my solution to solve my problem – Raj K Feb 15 '16 at 00:55