Retrieve HTTP Body in NanoHTTPD
Asked Answered
C

5

24

How can I retrieve the HTTP POST request body when implementing NanoHTTPDs serve method?

I've tried to use the getInputStream() method of IHTTPSession already, but I always get an SocketTimeoutException when using it inside of the serve method.

Contemplate answered 12/3, 2014 at 11:15 Comment(1)
How can I get parameters from the URL using the GET method?Adelaideadelaja
O
33

In the serve method you first have to call session.parseBody(files), where files is a Map<String, String>, and then session.getQueryParameterString() will return the POST request's body.

I found an example in the source code. Here is the relevant code:

public Response serve(IHTTPSession session) {
    Map<String, String> files = new HashMap<String, String>();
    Method method = session.getMethod();
    if (Method.PUT.equals(method) || Method.POST.equals(method)) {
        try {
            session.parseBody(files);
        } catch (IOException ioe) {
            return new Response(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage());
        } catch (ResponseException re) {
            return new Response(re.getStatus(), MIME_PLAINTEXT, re.getMessage());
        }
    }
    // get the POST body
    String postBody = session.getQueryParameterString();
    // or you can access the POST request's parameters
    String postParameter = session.getParms().get("parameter");

    return new Response(postBody); // Or postParameter.
}
Oceangoing answered 19/4, 2014 at 14:59 Comment(2)
any reason session.parseBody(files) has to be called before session.getQueryParameterString() ?Pachston
@Pachston I had a quick look at the source code: when dealing with non multipart/form-data requests, session.parseBody(files) reads the POST request's body and calls HTTPSession's decodeParms() method, which sets the queryParameterString and parms field values to the POST's body. Without the call to session.parseBody() these fields are empty.Oceangoing
M
32

On a IHTTPSession instance you can call the .parseBody(Map<String, String>) method which will then fill the map you provided with some values.

Afterwards your map may contain a value under the key postBody.

        final HashMap<String, String> map = new HashMap<String, String>();
        session.parseBody(map);
        final String json = map.get("postData");

This value will then hold your posts body.

Code that does this, can be found here.

Mulley answered 17/12, 2014 at 15:30 Comment(6)
By far the best solution, especially when using JSON.Mendelssohn
take note that if the Content-Type is application/x-www-form-urlencoded, the content of the parameters is found under session.getParms() instead of the map object that you supplyGunshot
The only solution that worked for me when the content-type is application/jsonAlidus
@Rinmalavi thanks for posting the link to the code where it is clear files.put("postData", postLine); one will need to use postData to get the data...but does any have any links to this in document form?Pickaxe
@Pickaxe From what I remember using this back in '14, documentation was mostly code itself. Not sure if the things changed. Sorry for no answer.Mulley
How do you mock sesssion.parseBody(map)Boman
G
12

I think session.getQueryParameterString(); not work in this case.

If you using POST, PUT, you should want to try this code:

Integer contentLength = Integer.parseInt(session.getHeaders().get("content-length"));
byte[] buffer = new byte[contentLength];
session.getInputStream().read(buffer, 0, contentLength);
Log.d("RequestBody: " + new String(buffer));

In fact, I tried IOUtils.toString(inputstream, encoding) but it cause Timeout exception!

Geniculate answered 12/8, 2015 at 6:39 Comment(2)
session.getHeaders().get("content-length") == null?Cardiff
this is better than others when post data is not in plain string formatFortson
P
0

This is how I am getting my post response body with NanoHttp, and it works for me. Very important to note that if you are handling your own error response codes and want to send a body use the error input stream instead of the conn.getInputStream() This will avoid the file not found exception or broken pipe exception if close the connection before the server sent the body.

 public HashMap<String, Object> getResponse(HttpURLConnection conn) throws IOException {

        Log.i("STATUS", String.valueOf(conn.getResponseCode()));
        Log.i("MSG", conn.getResponseMessage());

        StringBuilder response = new StringBuilder();

        String line;
        BufferedReader br;

        if (conn.getResponseCode() == HttpsURLConnection.HTTP_OK)
             br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

        else
            br = new BufferedReader(new InputStreamReader(conn.getErrorStream()));

        while ((line = br.readLine()) != null)
            response.append(line);

        conn.disconnect();
        return new Gson().fromJson(response.toString(), HashMap.class);
Phenformin answered 2/9, 2021 at 15:14 Comment(0)
K
0

I've used this method and worked for me without socket exception.

String body = "";

try {
    Map<String, String> headers = session.getHeaders();
    if (headers == null || headers.get("content-length") == null) {
        iDoMethodInThread.onException(new Exception("No Content length found in header"));
        return;
    }
    int contentLength = Integer.parseInt(headers.get("content-length"));
    byte[] buffer = new byte[contentLength];
    int totalRead = 0;
    while (totalRead < contentLength) {
        int read = session.getInputStream().read(buffer, totalRead, contentLength - totalRead);
        if (read == -1) {
            break;
        }
        totalRead += read;
    }
    body = new String(buffer, StandardCharsets.UTF_8);
} catch (IOException e) {
    e.printStackTrace();
}
Kashmiri answered 16/4, 2023 at 6:44 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Priceless

© 2022 - 2024 — McMap. All rights reserved.