60

I'm trying to get the post data in Java. Seems like it should be one of the simplest things to do right? I mean, HttpServletRequest.getParameter has to do it right? So how can you get the raw post data?

I found HttpServletRequest get JSON POST data and used Kdeveloper's code to pull the post data from a request. It works, but theres a catch: I can only get that post data once.

Heres the method I made from Kdeveloper's code:

public static String getPostData(HttpServletRequest req) {
    StringBuilder sb = new StringBuilder();
    try {
        BufferedReader reader = req.getReader();
        reader.mark(10000);

        String line;
        do {
            line = reader.readLine();
            sb.append(line).append("\n");
        } while (line != null);
        reader.reset();
        // do NOT close the reader here, or you won't be able to get the post data twice
    } catch(IOException e) {
        logger.warn("getPostData couldn't.. get the post data", e);  // This has happened if the request's reader is closed    
    }

    return sb.toString();
}

Previously I had closed the reader at the end of this method, but that caused exceptions when the method ran more than once on the same request. Without closing it, no exceptions happen, but the method returns an empty string.

Honestly, there should just be an exposed req.getPostData() method - did no one think that would be useful?

So how can I write this method such that it always returns the correct post data?

Community
  • 1
  • 1
B T
  • 57,525
  • 34
  • 189
  • 207

3 Answers3

91

The request body is available as byte stream by HttpServletRequest#getInputStream():

InputStream body = request.getInputStream();
// ...

Or as character stream by HttpServletRequest#getReader():

Reader body = request.getReader();
// ...

Note that you can read it only once. The client ain't going to resend the same request multiple times. Calling getParameter() and so on will implicitly also read it. If you need to break down parameters later on, you've got to store the body somewhere and process yourself.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • 3
    So your answer is that there is *no* way to do what I want to do? Its not about the client sending it more than once. The HttpServletRequest clearly stores the post data internally somewhere (as you can always get post parameters multiple times). I appreciate the answer, I just want to fully understand if you're saying "its impossible" or if you're just reitterating what i've already found out. – B T Feb 17 '11 at 00:23
  • 14
    On first `getParameter()` call, the `HttpServletRequest` will internally use `getInputStream()` to read and parse the request body (it's a byte stream from a network connection) and store it in a map which you can get by `getParameterMap()`. After this point, you can't read the request body by `getInputStream()`/`getReader()` anymore, because it's already been read. If you clarify the functional requirement behind this need more, then we may be able to suggest you better ways to achieve it. – BalusC Feb 17 '11 at 00:25
  • We have a medium sized system which passes around HttpServletRequest objects everywhere, and we have had less-than-accurate ways of displaying what the post data is (previously we've used a technique that compares the parameter map to the query string and assumes the difference is post data). Most of these things are internal logs, but we also have a case where a partner is sending us xml as post data. In this case, using getReader to get the post data will sometimes screw up our logging, and sometimes our logging will screw up the handling of the actual request. – B T Feb 17 '11 at 00:32
  • 9
    Well, you may want to create a `HttpServletRequestWrapper` which holds a copy of the request body in a `ByteArrayInputStream`. – BalusC Feb 17 '11 at 00:36
  • My thoughts exactly, but that could end up being a whole lot of code changes. I'm holding out hope for some genius who knows how to solve this problem. – B T Feb 17 '11 at 00:38
  • 6
    Here's an example of a request Wrapper you're looking for: http://stackoverflow.com/questions/1046721/accessing-the-raw-body-of-a-put-or-post-request – Tim Lewis Apr 04 '11 at 17:06
  • 1
    Thanks for the answer. "only read it once"...gotta say I'm pretty surprised by this design decision. – Steven Wexler Jul 27 '15 at 18:41
  • I would have thought HttpServletRequest would be nice enough to cache it under the hood while the request was open. Seems like keeping a cache of (usually small) request bodies would handle most use cases. Maybe I'm missing something... – Steven Wexler Jul 27 '15 at 18:49
  • How can one inspect the request (header and body) in IntelliJ debugger? – morpheus Jan 12 '16 at 22:33
7

We had a situation where IE forced us to post as text/plain, so we had to manually parse the parameters using getReader. The servlet was being used for long polling, so when AsyncContext::dispatch was executed after a delay, it was literally reposting the request empty handed.

So I just stored the post in the request when it first appeared by using HttpServletRequest::setAttribute. The getReader method empties the buffer, where getParameter empties the buffer too but stores the parameters automagically.

    String input = null;

    // we have to store the string, which can only be read one time, because when the
    // servlet awakens an AsyncContext, it reposts the request and returns here empty handed
    if ((input = (String) request.getAttribute("com.xp.input")) == null) {
        StringBuilder buffer = new StringBuilder();
        BufferedReader reader = request.getReader();

        String line;
        while((line = reader.readLine()) != null){
            buffer.append(line);
        }
        // reqBytes = buffer.toString().getBytes();

        input = buffer.toString();
        request.setAttribute("com.xp.input", input);
    }

    if (input == null) {
        response.setContentType("text/plain");
        PrintWriter out = response.getWriter();
        out.print("{\"act\":\"fail\",\"msg\":\"invalid\"}");
    }       
djabraham
  • 766
  • 11
  • 20
0

This worked for me: (notice that java 8 is required)

String requestData = request.getReader().lines().collect(Collectors.joining());
UserJsonParser u = gson.fromJson(requestData, UserJsonParser.class);

UserJsonParse is a class that shows gson how to parse the json formant.

class is like that:

public class UserJsonParser {

    private String username;
    private String name;
    private String lastname;
    private String mail;
    private String pass1;
//then put setters and getters
}

the json string that is parsed is like that:

$jsonData: {    "username": "testuser",    "pass1": "clave1234" }

The rest of values (mail, lastname, name) are set to null