Spark: Request#body() empty in Route if previously accessed in a Filter

Created on 9 Oct 2014  路  17Comments  路  Source: perwendel/spark

If Request#body() is called in a Filter mapped for a HTTP request, subsequent calls in the Route for this request will return an empty string.

I suspect this is simply to due to a new Request being created for each processing stage, and the previous read exhausting the corresponding InputStream in the HttpServletRequest.

Most helpful comment

This is not working in "before" filters too. After filter is working OK, but in before, req.body() is lost after first "consummation".

All 17 comments

Just got this issue today.

The main problem in the way we take body from underlying servletRequest

in Request.java you can find:

private void readBody() {
        try {
            bodyAsBytes = IOUtils.toByteArray(servletRequest.getInputStream());
            body = IOUtils.toString(new ByteArrayInputStream(bodyAsBytes));
        } catch (Exception e) {
            LOG.warn("Exception when reading body", e);
        }
    }

And there is the documantation from oracle:
link to Oracle Documentation!

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.

@perwendel can you suggest the best solution to fix that? This issue affects one of the best feature of Spark.

i will take a look at this over the weekend to see what can be done about it.

i think as a work around you could put the request body into the respone after you read it in the filter and then read it back out of the response (and reset it, change it, etc) in the route itself.

i'm working on a real fix but it's based on work i did for issue #137 - however given you're on the static interface, you should be able to easily try it out. i can let you know when i'm done.

Same problem for headers by the way. Here's my current workaround: I read only once and set it in an attribute:

public static String getUserId(Request request) {
    // Try to get from session
    String userId = request.session().attribute(USER_ID);
    if (userId == null) {
        // If necessary, get it from header and save it in session
        userId = request.headers("myapp-user-id");
        request.session().attribute(USER_ID, userId);
    }
    return userId;
}

i don't see anything in docs that indicate you can't read the headers more then once, so this may be caused by something else.

i fixed this in the branch i am working on but @perwendel seems to be mia and i don't know how these changes are going to be rolled out. i've been pushing snapshots to my oss repository but that's not a good long term solution.

Sorry, I've been extremely busy lately. @jgangemi Do you have a fix? I can't find anything in the pull request list.

i do but it's built on the changes for #137. i can probably split that into something separate and then we can figure out the larger issue of how to incorporate the work i did to break jetty out.

@jgangemi sounds like a good idea. I'll try to make the time to have a look ASAP when it's done.

@perwendel can you publish to maven central repo once it's done? We are now all using our own fork or pointing to a personal repo :)
Is there someone you trust that you could give rights on the repo to help you?
Spark java is awesome and super light, we want to keep using it and avoid forking it :)

sorry for the delay on this, a little busy myself. i will get this done over the weekend.

How about caching it as a special request attribute e.g. "spark.cachedBody" ? This is more friendly to further control, I think.

And based on this, how about also providing a no-cache method, just purely reading the body? Since caching is memory-consuming, especially when uploading files.

I've created a pull request that should fix this issue, since it's in the middle of the night and I have to go to bed I'd be happy if someone could review and verify it. All the tests pass.

https://github.com/perwendel/spark/pull/218

218 merged.

This is not working in "before" filters too. After filter is working OK, but in before, req.body() is lost after first "consummation".

Same problem here with befores and body coming with an empty string (i am using two befores), it is some work around?

Was this page helpful?
0 / 5 - 0 ratings