Spark: Accessing query param before getting body causes body become ""

Created on 20 Sep 2016  路  12Comments  路  Source: perwendel/spark

caseController has following function

public Response addCase(String json, boolean checkEnabled);

Here is problem

public static void main(String args[]) {
    port(8080);
    // other codes
    // ...
    post("/cases", (request, response) -> {     
        boolean checkEnabled = true;
        String checkEnabledParam = request.queryParams("check_enabled");

        if (checkEnabledParam != null && checkEnabledParam.contentEquals("false"))
            checkEnabled = false;

        String body = request.body();  // body=""
        return caseController.addCase(body, checkEnabled);
    });
}

If I move String body = request.body(); upper, problem resolves

public static void main(String args[]) {
    port(8080);
    // other codes
    // ...
    post("/cases", (request, response) -> {     
        boolean checkEnabled = true;
        String body = request.body();  // body="..."
        String checkEnabledParam = request.queryParams("check_enabled");

        if (checkEnabledParam != null && checkEnabledParam.contentEquals("false"))
            checkEnabled = false;

        return caseController.addCase(body, checkEnabled);
    });
}
Bug Minor 3.0 candidate ?

All 12 comments

A simple example can't reproduce:

        Spark.port(8080);
        Spark.post("/foo", (request, response) -> {
           request.queryParams("foo");
           String body = request.body();
           return body;
        });

Can you try to strip your code to the bare minimum that reproduces the problem? Does this happen in embedded mode or when deployed on a server?

Also (from getParameterMap() that I mistakenly first referred to):

Request parameters are extra information sent with the request. For HTTP servlets, parameters are contained in the query string or posted form data.

And (from getParameter() which is the actual method called):

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.

Could that result in the input stream being processed and not reset, ie. there no content remaining in the stream, when Spark tries to read the body?

Finally, I was able to write minimal program, that reproduces the bug.

import static spark.Spark.*;

public class Application {
        public static void main(String args[]) {
                port(8080);

                post("/bug", (request, response) -> {

                        boolean shouldBeMoon = request.queryParams("shouldBeMoon") != null;

                        String body = request.body();

                        return  "-----------------------\n" +
                                "shouldBeMoon: " + String.valueOf(shouldBeMoon) + "\n" +
                                "body: \n" + body + "\n" +
                                "------------------------";
                });

                post("/no_bug", (request, response) -> {

                        String body = request.body();

                        boolean shouldBeMoon = request.queryParams("shouldBeMoon") != null;

                        return  "-----------------------\n" +
                                "shouldBeMoon: " + String.valueOf(shouldBeMoon) + "\n" +
                                "body: \n" + body + "\n" +
                                "------------------------";
                });
        }
}

The bug is related to header of request Content-Type. Bug can be reproduced iff Content-Type: application/x-www-form-urlencoded, otherwise, it works fine.

This makes sense - in order to get the form parameters, request body is read, which then interferes with getting the body later on. Basically has to do how the application server handles things and is not really a Spark issue - unless @perwendel thinks this "should just work" which probably would mean Spark making a defensive copy of the request body?

Hm... I'm tempted to say it "should just work". @perwendel ?

@jakaarl Could you check if this is a lot of work (and if not create a PR?)

Today, I encountered the same problem. I was trying to get API key from query param in a filter and then use body in Route. Unfortunately, body is always an empty string.

Hmm. It does strike me as odd to pass values to the backend as a form with application/x-www-form-urlencoded and AT THE SAME time pass other values as query parameters. As if one cannot make up h(er|is) mind. What does the HTTP standard say about this? Does somebody know?

I created very simple webpage with jQuery to demo my backend to a client.

$.ajax({
  type: 'POST' ,
  url: '/foo',
  headers: {
    'X-API-Key': apiKey
  },
  data: xml,
  dataType: 'text'
})

in this situation jQuery sets Content-Type: application/x-www-form-urlencoded; charset=UTF-8. I was very surprised when body was set to empty string. To fix it I need to specify contentType: 'application/xml' in $.ajax. In my app you can add apiKey to query params or to the X-API-Key header.

Problem is, that the input stream does not necessarily support mark/reset, so we can't simply reset it. Instead, we'd have to wrap the servlet request in Request to intercept any calls to getInputStream() or getReader() in order to provide a stream or reader which for example copies on read in order to provide a "fresh" stream or reader on subsequent calls. Or does someone have ideas for an easier workaround?

OTOH, this is just the way it works: application/x-www-form-urlencoded basically sends query parameters in the body - they are available as query parameters, as they should. The content type indicates that. If params and a body are both needed, shouldn't the request be multipart/form-data?

Hello,
I use spark 2.8.0 and I have the same issue.

Spark.before((request, response) -> { RestfulMockLog log = new RestfulMockLog("LOG", request.ip(), request.body()); });

If i try to do request.body() , the result is always empty. It's a bug, anyone could help me, I'm using spark also in production enviroment

Been dealing with this bug for a bit... we use spark in production and found a very odd workaround to this problem.

Minimal sample code to reproduce:

    public static void main(String[] args) {

        port(8888);

        before((request, response) -> {
            String queryParam = request.queryParams("someQueryParam");
            System.out.println("Before 1: " + queryParam);
        });

        post("/", (req,res) -> {
            return "\n\nBody: " + req.body() + "\n\n";
        });

    }

curl -X POST -d 'hello' localhost:8888

Workaround:

    public static void main(String[] args) {

        port(8888);

        before((request, response) -> {
            request.body();
            String queryParam = request.queryParams("someQueryParam");
            System.out.println("Before 1: " + queryParam);
        });

        post("/", (req,res) -> {
            return "\n\nBody: " + req.body() + "\n\n";
        });

    }

For some reason accessing the request body in the before filter stops it from coming up as an empty string in the post method. So in our code, anywhere we have a filter that accesses the QPs in a before filter, we have a ridiculous looking request.body() with comments saying //Don't delete this

Was this page helpful?
0 / 5 - 0 ratings