Lecture 6.1 (Tuesday, Feb. 23, 2016)

  • Return quiz 1
  • Quiz 2 is on Thursday (we will talk at end of class about what is on it)
  • Moving forward with HTTP

Walk through exam solutions

HTTP Response Codes

When all is well, we get a 200 OK response from the server:

$ curl -v http://md.ekstrandom.net/robots.txt
*   Trying 54.174.89.232...
* Connected to md.ekstrandom.net (54.174.89.232) port 80 (#0)
> GET /robots.txt HTTP/1.1
> Host: md.ekstrandom.net
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Mon, 22 Feb 2016 16:13:00 GMT
< Server: Apache/2.2.15 (Red Hat) PHP/5.4.40
< Last-Modified: Mon, 05 Oct 2015 00:26:10 GMT
< ETag: "221fed24-30-521508f5eac80"
< Accept-Ranges: bytes
< Content-Length: 48
< Content-Type: text/plain
< Vary: Accept-Encoding
<
User-agent: *
Disallow: /s/
Disallow: /private/

In the curl output, lines prefixed with > are the request sent to the server, and < indicates response headers.

Let's review the parts of the request and response:

Request line
This contains the method (GET), the path (/robots.txt), and the protocol version HTTP/1.1.
Request headers
These contain additional information to help produce the response.
Response line
Contains the HTTP version (HTTP/1.1), response code (200), and reason (OK).
Response headers
Contain additional data about the response, such as the length and content type.
Response body
After the empty < line, contains the actual response content.

You've probably already seen a couple of other codes:

  • 500 when there's a Python problem
  • 404 when you're visiting a URL with no route

404 is the generic ‘nothing to see here’ response. It means ‘not found’.

Classes of HTTP Codes

To review:

1xx
Informational; not used very often.
2xx
OK (or various versions of it)
3xx
The request was OK, but the data requested is elsewhere.
4xx
There was an error in the request.
5xx
The server had a problem handling the request.

Implementing 404 Codes

Let's consider the Animals example code again.

Suppose we write URL to view an animal that does not exist.

It'll 500.

But we would rather 404, since that is what really happened. To do this, we use flask.abort:

@app.route('/animals/<int:aid>')
def show_animal(aid):
    if aid < 0 || aid >= len(animals):
        flask.abort(404)
        # 'abort' throws an exception, so code will stop here
    return flask.render_template('animal.html', animals[aid])

The abort function allows us to fail with any standard HTTP error code. For example, if you know that an item used to exist but it no longer does, you can fail with 410 to send a ‘Gone’ response.

Redirects

We've seen the 200s — everything's good — and the 400s, particularly ‘Not Found’.

The 3xx class of responses say look elsewhere. The common ones are:

301
Moved Permanently — the requested resource is located somewhere else, permanently.
302
Found — the requested resource is elsewhere, but future requests should still use this URL.
303
See Other — used in form handling (we'll see this in a bit).

All of these return a Location header that specifies the new URL.

The difference between 301 and 302 is in its permanency: if a URL returns 301, then it is fine for the browser to always use the new URL; if it's 302, it should keep using the current URL. It is the difference between ‘content moved’ and ‘content somewhere else, that might change, but I'll keep you updated’.

Sending Redirects

To send a redirect, return the results of calling flask.redirect.

Let's create a permanent redirect for an animal:

@app.route('/a/<int:aid>')
def animal_alias(aid):
    return flask.redirect(url_for('show_animal', aid=aid), code=301)

Forms

Let's add a new HTML element: forms.

Forms let us send data to the server.

Let's make a really basic query form to look for animals:

<form action="/search">
<label>Name: <input type="text" name="name" placeholder="animal"></label>
<button type="submit">Search</button>
</form>

There are a few pieces:

form
This element contains the form.
action
The URL that will handle the form's data.
label
A user-visible label for a form widget.
input
An input field; in this case a text field for the animal name.
button
A button to perform some action, in this case submitting the form.

When this form is submitted with an animal name of wombat, it will take the user to the URL

/search?name=wombat

The key-value pairs after the ? form the query string, and are available in flask.request.args (as a dictionary).

Let's handle this:

@app.route('/search')
def search():
    name = flask.request.args['name']
    # do something with 'name'

POST requests

The kind of form we have seen is good for doing things like search.

But what if the user needs to submit data that should make a change on the server?

For various reasons, this should not be done in a standard GET request:

  • GET requests can be cached, so they might not actually happen.
  • GET requests can be repeated, so they might happen twice.

The HTTP specification indicates that GET requests must be idempotent: that is, the same request twice should return equivalent data.

There is another method, POST, that is for making changes. To use it:

  1. Change the form to have method="POST"
  2. Change the app.route to say methods=['POST'] for only handling POST, or methods=['GET', 'POST'] to handle both in the same function.
  3. The POST handler should not just return a page: instead, it should return a 303 redirect to a page that shows the results of the action.

Note

The 303 redirect is so that the user can refresh the results page without resubmitting the POST action.

Let's make a little thing that adds to a list of items.

Quiz 2

  • Anything from previous quizzes!
  • CSS selectors
  • CSS inheritance
  • HTTP status codes (and methods?)