<<<question/reply from dirk/andrew copied from separate mail thread>>>
> 4)
> JSON
> I noticed that JSON calls, used by the new Stripes.submitFormEvent, requires
> the 'evalResponse' to be set to 'true'.
> This is because the ajax bean returns a piece of executable javascript;
> rather than a passive json object.
> (this is generated via the Stripes "JavaScriptResolution" class.
>
> I'd rather prefer that the server returns a passive JSON object, to avoid JS
> injection security issues.
> In this case, it should just return a simple string: { result:"long string"
> } There is no need for more complex data passing.
> WDYT ?
I'm glad we are talking about this. I hoped we would sooner or later,
because you are our resident JavaScript guru. :)
The goal with Stripes.submitFormEvent was to cause a StripesActionBean
form event to be called, and to provide way for any errors or messages
(plus the response) to propagate back to the client. We call the
ActionBean event by submitting the form in the background (AJAX) and
supplying an event to execute. The response object has three
properties:
- the response itself (which for simplicity's sake I have been
assuming would ALWAYS be HTML)
- the "errors" string (which we generate in the same manner as the s:errors tag)
- the "messages" string (which we generate in the same manner as the
s:messages tag)
All three of these are concatenated together, wrapped in a <div>, and
injected into the div we want. That means any Stripes validation
errors or messages we get from the AJAX call are propagated back to
the browser. Neat, huh?
If you want to see this in action, by the way, just run the Installer
and try to configure the LDAP userdatabase. Because you (probably!)
don't have an LDAP server running locally, when you test the
connections you'll get an error. That error is propagated back and
displayed via Stripes.submitFormEvent.
Now, in terms of HOW that information is propagated back, I chose to
send back a JavaScript statement that could be eval'ed. This is
accomplished by the EventResolution class, which essentially wraps the
JavaScriptResolution response type, and does some nice formatting of
the errors and messages stuff. I did this because it was the simplest,
fastest way I knew how.
I don't really care how the information is propagated back to the
client. If JSON is easier, that's fine. I'd ask that if you do this,
please make appropriate changes to EventResolution and
Stripes.submitFormEvent so that we can do it everywhere.
*******
I do care. Passing "text/javascript' back to the client, will trigger
a javascript execution on the client.
While this can be usefull for very complex client functionality, it is
also vulnerable to xss.
In this particular case, I would prefer not to return a
'text/javascript' message, but rather return 'application/json' as
content-type.
Just return the object with 3 strings, the handling is the same:
{ 'errors' : html-string, 'messages': html-string, 'result': html-string }
Also, note that the returned 'text/javascript' is immediately executed
at the client side.
This will generate unnecessary GLOBAL javascript vars on the client
for each ajax request.
*******
Some suggested guidance if you want to change the "message transport" to JSON:
- EventResolution, NOW, wraps a JavaScriptResolution that emits the JS
object. You will, instead, want to wrap and return a
StreamingResolution that emits an JSON-notated object.
Here's the JavaDoc for StreamingResolution:
http://stripes.sourceforge.net/docs/current/javadoc/net/sourceforge/stripes/action/StreamingResolution.html
*******
In the current svn you are using the JavaScriptBuilder class; the
JavaScriptResolution class would be a slight simplification.
While the included Stripes classes provide poweful functionalities to
convert JAVA to JSON objects,
they unfortunately always package the JSON into some unnecessary
javascript enclosing.
Here is what Stripes returns:
var eventResponse;
var _sj_13986377 = {errors:null, messages:null, results:" .... "};
eventResponse = _sj_13986377;
eventResponse;
I do not understand why a simple 'buildJSON' is not part of the API of
their JavaScriptResolution class.
*******
- If possible, I'd like to preserve the default client-side behavior
of Stripes.submitFormEvent -- where if there is NO callback function
supplied, the default behavior is to concatenate the errors, messages,
and response together and inject the whole thing into the target div.
But if there IS a supplied callback function, we call that instead
(which can do whatever it wants).
*******
Agree. The possibility of a callback function provides lots of flexibility.
I would also suggest to add the div-target in the callback, like this:
callback( eventResponse, divTarget )
One more thing.
I noticed that the call to Stripes.submitformEvent is coded inside the JSP.
In the past, I've tried to keep the JS dependency in the HTML as
little as possible,
and keep all javascript inside the *.js files.
This means that the AJAX handlers are injected in the DOM-tree during
page-load, by the javascript itself.
It also means that if javascript is not on, the page falls back to a
regular html links, iso of AJAX calls.
EG, in the EdititorLayout.jsp you find following snippet:
<wiki:Tab id="preview" titleKey="preview.tab" accesskey="p"
onclick="Stripes.submitFormEvent('editform', 'preview',
'previewContent', null);">
<div class="information">
<fmt:message key="preview.info" />
</div>
<div id="previewContent"></div>
</wiki:Tab>
Instead we could better go for this:
<wiki:Tab id="preview" titleKey="preview.tab" accesskey="p"
url=" ...the preview url : Edit.jsp?preview&... " >
<div class="information">
<fmt:message key="preview.info" />
</div>
<div id="previewContent"></div>
</wiki:Tab>
During page-load, javascript would then inject onclick handers
overwriting the default behaviour of the ajaxed links.
For tabs, this could probably be build in a generic way, automatically
recognising the parent form, the enclosed target-elements, etc....
Anyway, in the current state we are in, this would not be on my priority list.
But in general, let's try to keep the amount of javascript in the jsp
pages as minimal as possible.
*******
- If it makes sense, maybe the "response" property of eventResponse
should be a JavaScript object instead of a string? If so, the default
behavior of Stripes.submitFormEvent (when there is no callback method
supplied), should safely coerce the response JS object into a String.
(That is a guess...)
******
The 'result' property of eventResponse is a string with HTML. Which is
perfectly ok.
******
dirk