Chapter 11 ■ the World Wide Web
198
When this HTML form is submitted, the browser places the data into the body of the request in its entirety,
leaving the path completely alone.
POST /donate HTTP/1.1
Host: example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
name=PyCon%20scholarships&dollars=35
Here you are not passively asking to go visit a “$35 for PyCon scholarships” page because you are interested in
looking at it. On the contrary. You are committing to an action—an action that will be twice as expensive and have
twice the impact if you decide to perform the POST twice instead of just once. The form parameters are not placed
in the URL because “$35 for PyCon scholarships” is not the name of a place you want to go visit. It is what the late
philosopher J.L. Austin would call a speech act, that is, words that cause a new state of affairs in the world.
There is, by the way, an alternative form encoding multipart/forms based on the MIME standard (Chapter 12)
that browsers can use for uploading large payloads like entire files. However, either way, the semantics of the POST
form are the same.
Web browsers are extremely cautious about POST precisely because they understand it to be an action. If the user
tries to click Reload while looking at a page returned from POST, the browser will interrupt them with a dialog box.
You can see one if you bring up the web application from Listing 11-2, visit its /pay form, and then submit the form
without typing anything so that it comes back immediately with the complaint “Dollars must be an integer.” When
I then click Reload in Google Chrome, a dialog box pops up.
Confirm Form Resubmission
The page that you're looking for used information that you entered. Returning to the page might
cause any action you took to be repeated. Do you want to continue?
You should see a similar warning in your own browser. While looking at the form with human eyes, you can see
clearly that the form submit does not seem to have taken effect; but the browser has no way of knowing that the POST
failed to have an effect. It sent a POST, it received a page, and for all it knows the page says something like “Thank you
for donating $1,000,” and the effect of submitting it again could be disastrous.
There are two techniques that web sites can use to avoid stranding the user on a page that is the result of a POST and
that therefore will cause endless trouble for both the Reload and the Forward and Back buttons in the user’s browser.
• Use JavaScript, or HTML5 form input constraints, to try to prevent the user from submitting
invalid values in the first place. If the submit button does not light up until the form is ready
for submission or if the entire form round-trip can be handled in JavaScript without the page
being reloaded, then an invalid submission—such as the empty form that you submitted a
moment ago—will not strand the user at a POST result.
• When a form is finally submitted correctly and its action succeeds, the web application
should resist the temptation to respond directly with a 200 OK page that describes the
completed action. Instead, respond with a 303 See Other redirect to another URL specified
in the Location header. This will force the browser to follow up the successful POST with an
immediate GET that lands the user somewhere else. The user can now hit Reload, Forward,
and Back to their heart’s content, resulting only in safe repeated GETs of the results page,
instead of repeated attempts to submit the form.
While the simple application in Listing 11-2 is too primitive to shield the user from seeing a POST result in the
case that the form is invalid, it does at least perform a successful 303 See Also powered by the Flask redirect()
constructor when either the /login form or the /pay form succeeds. This is a best practice for which you should find
support in all web frameworks.