History for GetAndPostToSamePage
??changed:
-
New Standard Idiot: GET and POST to same page
In the beginning there was Bobo and it was good. In our innocence,
we used pages to display the state of an objects with an editing
interface (e.g. manage_main), and methods to do the edit
(manage_edit). After performing an edit, we'd typically just call
the display page (manage_main) from manage_edit. This seemed to
work well for a while.
Later, we noticed that the display page would sometimes get confused
because it was displayed via the "wrong" URL. To get around this,
we started using redirects to make the browser visit the display
page after the edit. This has the obvious downside of causing a
separate request to be made. Also, some clients may not handle
redirects predictably.
An alternative would be to use the same URL for display and edit. A
display page could be written in such a way that if a request was a
POST, the edit method would be called internally before executing
the display logic. If there was an input error, then the form would
be displayed with the data from the request and any input error
messages.
A simple example.
Suppose we have an object that has an interface
for managing two properties, 'title' and 'text'. Title is a simple
string and text is a possibly-multi-line string.
We *might* have something like::
<html><body>
<dtml-form post="main_post"
errors="errors,title_error,text_error">
<dtml-if errors>
<hr><font color="red"><ul>
<dtml-in errors>
<li>&dtml-sequence-var;</li>
</dtml-in>
</ul></font><hr>
</dtml-if>
Title:
<dtml-if title_error>
<font color="red">&title_error;</font>
</dtml-if>
<br><input name=title value="&dtml-title;">
Text:
<dtml-if text_error>
<font color="red">&text_error;</font>
</dtml-if>
<br><textarea name="text:text">value="&dtml-text;">
<input type="submit">
</dtml-form>
</body></html>
We could do this without a new tag. But the example wouldn't be so
simple. ;)
Zen -- This example will of course raise a KeyError, as the
values for the input controls will not render during the
initial viewing of the form. The simple fix for this is to
add another attribute to the dtml-form, which specifies
a 'populate' object to populate REQUEST with required
values (eg. set them to empty strings, default values or
pull from a SESSION). We could also
move the automatic variable type checking into
the <dtml-form> tag.
For example::
<html><body>
<dtml-form post="main_post" validate="main_validate"
populator="SESSION"
controls="title,text:text,priority:int"
errors="errors,title_error,text_error">
<p>main_post will only be called if neither
main_validate or the Zope internal type checking
(priority:int) returns any errors.
<dtml-if errors>
<hr><font color="red"><ul>
<dtml-in errors>
<li>&dtml-sequence-var;</li>
</dtml-in>
</ul></font><hr>
</dtml-if>
Title:
<br><input name=title value="&dtml-title;">
Priority:
<br><input name=priority value="&dtml-priority;">
Text:
<br><textarea name=text value="&dtml-text;">
<input type="submit">
</dtml-form>
</body></html>
ChrisW -- I actually like the idea of the new tag. It makes things nice and clean :-)
The dtml-form tag:
- generates a HTML form tag, using the request URL
as the action.
- If the request method is POST:
o Makes sure that REQUEST!['HTTP_REFERER']==REQUEST!['URL'], to
protect against trojans. ;)
Could this be optional? Could you supply a list of
acceptable referers? I can think of ocassions when it's
useful to have two seperate forms handled by the same
action.
PJE -- not only that, but what do you do about clients which don't send referrers? What about XML-RPC POST operations? Maybe this should be implemented using different method names, e.g. GET and POST instead of index_html. POST could call GET if errors are present. No tags needed, cleaner seperation of functionality...?
o Securely calls the method given in the post attribute,
'main_post'.
Note -- We need a way of expressing that a method is
callable by DTML but not callable through the web....
And the other way around.
ChrisW -- I suggested this on the ZopeDev list on
11/12/99 as 'Execute permission thoughts...'
but didn't get any response :(
ChrisW -- This had another airing on the ZopeDev list recently (May 2000) and lots of interesting views were expressed. Maybe someone could pick one and implement it? ;-)
o If the result of calling POST is None:
- Adds the names mentioned in the "errors" attribute to the
namespace with empty strings as values.
Otherwise:
- Pushes REQUEST to the top of the namspace stack.
- Adds the names mentioned in the "errors" attribute to the
namespace with empty strings as values.
- Adds the attributes of the result of calling POST to the
namespace.
- If the request method is GET:
Adds the names mentioned in the "errors" attribute to the
namespace with empty strings as values.
I've been thinking of a facility like this for a while. What finally
pushed me over the edge was Emacs W3 v4.0 whining about a redirect
after editing a wiki page. Would this facility help? Hm. Well, not
exactly. With Wiki pages, there are three pieces:
- The normal view
- The edit view
- The edit action
Currently, you view the normal view, follow a link to the edit view,
POST to the edit action, which redirects to the normal view.
The POST would need to go to the normal view. If there aren't any
errors, then we display the nrmal view with updated data. If there
*are* errors, we would need to display the edit view with the
errors. This might be a bit weird, although I think I could live
with it.
The implementation would be a bit different, since the normal
view a Python method, not a DTML method. Also, the HTTP_REFERER
check would have to allow the normal view *or* the edit view.
<hr>
I just implemented a form in this way. Here's the code, as food for thought: GetAndPostExampleOne
--ZWikiWeb:SimonMichael
<hr>
JHT
I've done systems this way. Works fine from the perspective of the web server, but not so well from the perspective of the user.
What happens is that a user fills out a form and submits it.
They get back the results and then go to the next page.
Now if they try to use the back arrow to see the results again, the browser wants to repost the form before it will display the page again. The redirect scheme gets around this problem.