Flaskless

Although Flask is popular, it certainly isn’t necessary! Indeed, you can implement web applications using Python’s own built-in http library.

Consider the program, below, which implements a simple web application that (only) returns a static HTML page.

# Implements a web server

import os

from http.server import BaseHTTPRequestHandler, HTTPServer

class HTTPServer_RequestHandler(BaseHTTPRequestHandler):

    # Handle GET requests
    def do_GET(self):

        # Respond with status code
        self.send_response(200)

        # Respond with headers
        self.send_header("Content-type", "text/html")
        self.end_headers()

        # Respond with body
        self.wfile.write(b"""
            <!DOCTYPE html>

            <html lang="en">
                <head>
                    <title>hello</title>
                </head>
                <body>
                    hello, world
                </body>
            </html>
        """)

# Run server
port = 8080
httpd = HTTPServer(("0.0.0.0", port), HTTPServer_RequestHandler)
print(f"Running on https://{os.environ.get('C9_HOSTNAME')}:{port}")
print("Press CTRL+C to quit")
httpd.serve_forever()

If you copy and paste that code into a file called, e.g., hello.py, run python hello.py, and then visit the outputted URL, you should see a greeting!

Flask, of course, does much more than just return static HTML pages. Flask also “parses” HTTP requests, extracting from each request’s “path” the desired route as well as the names and values of any HTTP parameters. For instance, a path like

/search?q=cats

has a route of /search and an HTTP parameter named q, the value of which is cats. A question mark (?) separates the two.

If a path contains multiple parameters, those key-value pairs are separated by ampersands (&). For instance, if you visit amazon.com and search for “duck”, odds are you’ll find yourself at a URL like

https://www.amazon.com/s?k=duck&ref=nb_sb_noss_2

which has a route of /s and two parameters: k, whose value is duck, and ref, whose value is nb_sb_noss_2 (or similar).

Let’s implement parsing.

  1. (6 points.) In flaskless/server.py, complete the implementation of parse in such a way that the function parses path, returning a list whose first element is a str representing the path’s route and whose second element is a dict representing the path’s parameters. For instance, if path is

    /greet?first=Cody&last=Murphey
    

    then parse should return:

    ['/greet', {'first': 'Cody', 'last': 'Murphey'}]
    

    Assume that path will contain no more than one question mark. And assume that any parameters will have both a name and a value, neither of which will contain & or = (except for one = between them). If there is no question mark in path or if there are no parameters in path, then your dict should be empty. For instance, if path is just / or even /?, then parse should return:

    ['/', {}]
    

    You might find split of some help!


Flask also supports HTML templates, which allows us to replace placeholders in those templates with the values of variables. Recall that those placeholders are surrounded by {{ and }}.

Let’s implement templating.

  1. (6 points.) In flaskless/server.py, complete the implementation of render_template in such a way that it opens template, reads its contents, and replaces any placeholders therein with values from args. For instance, suppose that template is templates/greet.html and args is: {'first': 'Cody', 'last': 'Murphey'}. Then render_template should open templates/greet.html, read its contents, and replace any instances of {{ first }} therein with Cody and any instances of {{ last }} therein with Murphey, thereafter returning the resulting str. Assume that each {{ will be followed by a single space, followed by the name of a variable, followed by a single space, followed by }}. And assume that args will have keys (and values) for all variables in a template. No need to support {% and %}.

    You might find open, read, replace, and close (or with) of some help!

Once you have implemented both parse and render_template, you should be able to cd into flaskless and run:

python server.py

If you then visit the outputted URL, you should see a form (the contents of templates/index.html). If you submit that form, you should see a greeting!

If you make any changes to server.py thereafter, be sure to stop the server with CTRL+C and re-run it.