Hi Jon and others ...
I have been wrestling with this challenge with filters for a couple of days now. Here's a simple use-case:
- HTML web page (test.html).
- One input field ("name")
- Method = Post
- Before filter (this checks authentication)
- before( "/post/details", authenticator )
- Looks at session and tests if authenticated.
- When "YES" ... pass through to the "/post/details" handler.
- Log file output:
- Authenticator:
- pathInfo: /post/details
- Parameters: "name=George&save=" ( this is: request.body() )
- authenticated == true, so:
- set response body as follows: response.body( request.body() );
- I got the idea for this by stepping through the filter code with the debugger (details below).
- /post/details handler:
- pathInfo: /post/details
- Parameters: null ( this is: request.body() )
- Result my update fails.
Looking in Spark -- MatcherFilter.doFilter( ... ): Observe line #88. (Line numbers may differ depending on which version of the source you are looking over).
- String bodyContent = null;
The before filter(s) check the response.body() content after each filter finishes. Witness, line #112
- String bodyAfterFilter = Access.getBody(response);
if (bodyAfterFilter != null) {
bodyContent = bodyAfterFilter;
}
Once I saw this, filters started to make more sense for me. Each filter can make changes and pass the result, like a relay-baton to the next handler. Looking further down the page in the same method, around line #135, some place around the line
highlighted below I believe Spark needs to transfer the
bodyContent to the newly created
Request instance.
- if (target != null) {
try {
String result = null;
if (target instanceof RouteImpl) {
RouteImpl route = ((RouteImpl) target);
Request request = RequestResponseFactory.create(match, httpRequest); // label: [A]
Response response = RequestResponseFactory.create(httpResponse);
req.setDelegate(request);
res.setDelegate(response);
Object element = route.handle(req, res);
result = route.render(element);
// result = element.toString(); // TODO: Remove later when render fixed
}
}//end-try...
I was thinking something like:
if( null != bodyContent )
{
response.body( bodyContent );
}
In that way, the content changes or transformations from "filters" would propagate forward. If not, then my naive expectation was for the request passed to my "post/details" handler to have the original body content from the HTML page. Unfortunately, the newly created request (label [
A]) from the snippet above has a null body content.
I suggest that this is a big problem with filters. Until there's a code-update or a fix push-ed through the only thing before filters can be used for is is to redirect URL-s to different pages or check for errors like 404, etc. Take care about what you'd like to do with filters, the feature as written doesn't suit my use-cases where I'd like a filter (esp. authentication). Which is a great pity imho.
I'm going to check the GitHub to see if there's work in progress. I've posted here because it will save folks days of debugging effort to skip-by filters until there's a better way to process chained requests. It may not be a 'bug' -- Another reason to write something here first. As the documentation is minimal on this, I can't say how you're supposed to use filters for real applications, my assumptions/expectations may be off-base.
I'd like to hear if others have a more effective pattern for filtering, transforming and chaining requests.
Also I got a couple of InvalidStateExceptions while looking at filters. It would be good if one of you knows where the state changes happen along the data pipeline.
I'll be very interested to hear comments and suggestions. Oh yes, I was using Spark 2.0.0 and Java 8. I notice Jon's question is about Java 7 so the
challenge is with us. Thanks for listening, comments most welcome.
Kind regards,
Will