4

In a Grails 2.5.0 controller action method, it seems like the properties in the HTTP JSON body will not be used for command object binding if request.JSON has been accessed in a filter.

Why is that? It doesn't make any sense to me.

Is there any way to allow request.JSON to be used in a filter, and also for command object binding?

XDR
  • 4,070
  • 3
  • 30
  • 54

1 Answers1

3

Yes, this is the default behavior of Grails while it comes to data binding with request body. When you read the request body via request.JSON in your filter then the corresponding input stream will be closed or gets empty. So, now Grails can't further access that request body to bind to the command object.

So, you can either access the request body on your own in the filter or can use it with the command object but can't both.

From Binding The Request Body To Command Objects heading http://grails.github.io/grails-doc/2.5.0/guide/theWebLayer.html#dataBinding:

Note that the body of the request is being parsed to make that work. Any attempt to read the body of the request after that will fail since the corresponding input stream will be empty. The controller action can either use a command object or it can parse the body of the request on its own (either directly, or by referring to something like request.JSON), but cannot do both.

So, what are you trying to achieve is not possible directly. But, you can do something different. In your filter, read the incoming request body and store into the params or session (if filter passes the request to controller) and then manually bind the parameter in action:

MyFilters.groovy

class MyFilters {

    def filters = {
        foo(/* your filter */) {
            before = {
                // Your checks
                Map requestData = request.JSON as Map
                session.requestData = requestData
                return true
            }
        }
    }
}

Now, in your controller action, instead of doing:

class MyController {

    def fooAction(MyCommandObject object) {
    }
}

Do something like this:

class MyController {

    def fooAction() {
        MyCommandObject object = new MyCommandObject(session.requestData)
        // Clear from session to clear up the memory
        session.requestData = null
    }
}

Update: The above solution, I've provided will work well but is not clean. @JoshuaMoore provided a link with a cleaner solution Http Servlet request lose params from POST body after read it once.

Community
  • 1
  • 1
Shashank Agrawal
  • 25,161
  • 11
  • 89
  • 121
  • While this may work, it's not a solution that will scale well. Given the use of session data, and that you aren't ever clearing the data out of the session. You are far better off using a Servlet Filter that implements/extends `HttpServletRequestWrapper` for repeatable reading. This topic is well covered on Stackoverflow: http://stackoverflow.com/questions/10210645/http-servlet-request-lose-params-from-post-body-after-read-it-once ... Just because something works, doesn't mean it will work well. So, for a hobby project the answer here is fine, but for real-world apps it's short sighted. – Joshua Moore Jul 25 '15 at 18:57
  • Thank you @JoshuaMoore for your reply. The link you provided in awesome. I'd tried this months ago but didn't get success due to some lack of time. Yes, this is the cleaner solution from what I've provided as a temporary & quick fix solution. – Shashank Agrawal Jul 25 '15 at 20:31
  • You confirmed that command objects aren't populated if `request.JSON` is accessed in a filter, but I don't exactly understand why that is. If I remember correctly, I was still able to read properties from `request.JSON` in an action method whose command object wasn't populated because `request.JSON` was accessed in a filter. If all the JSON properties are still in `request.JSON`, why doesn't Grails use that to populate the command object. – XDR Jul 27 '15 at 07:10
  • Also, given the current behavior, no one should ever use command objects. If you write an action method, you can never control whether someone will use a filter that accesses `request.JSON`. It's a massive bug always waiting to happen. Given the limitations on command objects, they shouldn't be supported by Grails at all (or they should be modified to be independent of filter behavior). – XDR Jul 27 '15 at 07:13
  • I'm not sure if you will be able to access the `request.JSON` in the action if you have already read it in the filter. Since it is by implementation of Java's [ServletInputStream](http://docs.oracle.com/javaee/7/api/javax/servlet/ServletInputStream.html) which gets auto closed upon reading since it is implementing [AutoCloseable](http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html?is-external=true). So, this is not a Grails implementation. Although, Grails should have handled this in a way as it described in the above StackOverflow link to work it smoothly. – Shashank Agrawal Jul 27 '15 at 07:58
  • new MyCommandObject(session.requestData) won't work if there is a Date field. – Douglas Mendes Jan 22 '16 at 11:07
  • Yes, you can use the grails date binding feature for it. – Shashank Agrawal Jan 22 '16 at 11:08