Flask 1
Note: delete all other stuff running in background

help docs in idle:
- from flask import Flask
help(flask)
# p01_server_without flask.pyUse sockets, HTTP, HTML, CSS to generate web page with random colours and text every time it is requested Run code, then view http://127.0.0.1:8000/ ⇒ refresh to see randomised changes import randomimport socket# HTTP uses \r\n to end lines instead of \nEOL = b'\r\n'# list of possible coloursCOLOURS = [‘red’, ‘orange’, ‘blue’, ‘purple’, ‘#C0C000’, # dark yellow ‘#00C000’ # dark green ] # list of possible messagesMESSAGES = ['Hello, World!', 'Computing is Fun', 'Jinjjaaa']# server start listening for web browser requests on port 8000listen_socket = socket.socket()listen_socket.bind((‘127.0.0.1’, 8000)) listen_socket.listen() def handle_request(new_socket):# keep loading request until end of first line (\r\n)request = b''while EOL not in request:received = new_socket.recv(1024)# when recv() returns empty bytes object# close connection so program stops processing request immediatelyif received == b'':new_socket.close() returnrequest += received # extract 1st line & split into 3# requested path is always the 2nd part# ==> extract path component from 1st line of requestindex = request.index(EOL)first_line = request[:index].decode()path = first_line.split()[1]# start new HTTP response with standard HTTP status lineresponse = b'HTTP/1.1 200 OK' + EOL# generate CSS document for response if path is '\css'# else, generate HTML document for responseif path == '/css':# generate css documentborder_colour = random.choice(COLOURS)text_colour = random.choice(COLOURS)body = 'p {{ border: 5px solid {0}; color: {1};'body += ‘font-size: 72px; padding: 20px; }}‘ body = body.format(border_colour, text_colour)body = body.encode()# generate and append HTTP header fields to responseresponse += b’Content-Type: text/css’ + EOL response += b’Content-Length: ’ response += str(len(body)).encode() + EOL else:# generate HTML documentmsg = random.choice(MESSAGES)body = '<!DOCTYPE html> \n<html>'body += ‘<head><title>{0}</title>’ body += ‘<link rel=“stylesheet” href =“/css”></head>’ body += ‘<body><p>{0}</p></body></html>‘ body = body.format(msg).encode()# append HTTP header fields to responseresponse += b’Content-Type: text/html’ + EOL response += b’Content-Length: ’ response += str(len(body)).encode() + EOL # append empty line to response (header field)response += EOL # append document to response as message bodyresponse += body # send completed response back to web browsernew_socket.sendall(response) # close socket so server can handle the next HTTP requestnew_socket.close() while True: # until Ctrl-C is pressed# accept HTTP request sent when browser visits http://127.0.0.1:8000/new_socket, addr = listen_socket.accept()handle_request(new_socket) |
|---|
⇒ complex, tedious, error-prone
- Must parse or construct HTTP request / status lines & header fields to follow HTTP standard
- Must assemble long pieces of HTML and CSS code from Python strings
⇒ Should use a framework instead
- = module or library with ready-made generic solutions that a programmer can selectively override to customise certain behaviours
- Gets more done in less time as the frame work already provides a working solution; programmer only needs to configure or customise for his or her needs
| Run web server at http://127.0.0.1:5000/ Repeats the 1st line of each HTTP request back to browser E.g. URL | Output http://127.0.0.1:5000/ | GET / HTTP/1.1 http://127.0.0.1:5000/hello | GET /hello HTTP/1.1 http://127.0.0.1:5000/200/300/ | GET /200/300/ HTTP/1.1 http://127.0.0.1:5000//300//400// | GET //300//400// HTTP/1.1 http://127.0.0.1:5000/?key=value | GET /?key=value HTTP/1.1 import socketEOL = b'\r\n'listen_socket = socket.socket()listen_socket.bind((‘127.0.0.1’, 5000)) listen_socket.listen() def handle_request(new_socket):request = b''while EOL not in request:received = new_socket.recv(1024)if received == b'':returnrequest += received index = request.index(EOL)first_line = request[:index]response = b'HTTP/1.1 200 OK' + EOLresponse += b’Content-Type: text/plain’ + EOL response += b’Content-Length: ’ response += str(len(first_line)).encode() + EOL response += EOL response += first_line new_socket.sendall(response) new_socket.close() while True:new_socket, addr = listen_socket.accept()handle_request(new_socket) |
|---|
Flask Framework
- Not built into Python ⇒ pip3 install flask; then import flask
- HTTP requests and routing
- How HTTP request path is mapped / routed to Python function
- Fixed routes
- Variable routes
- Routing by HTTP Methods
- HTTP Responses and Status Codes
- Changing status code
- Changing the response headers
- Redirecting to another URL
- ⇒ Creates dynamic web pages
# p02_minimal.pyRun basic web server that Flask provides Implements HTTP and its many requirements Create a flask.Flask object with the module’s __name__ as an argument Call the object’s run() method import flaskapp = flask.Flask(__name__)if __name__ == '__main__':app.run() ⇒ when run, start-up message should indicate that the server can be accessed at http://127.0.0.1:5000/ Since default web server is not configured to recognise any paths yet, will receive a 404 (Not Found) error when visit that URL using web browser But notice that Flask alr provides a complete web server that correctly implements HTTP without additional work from programmer 5000 is default port number used by Flask To use another port number, use e.g. app.run(port=12345) To stop Flask server, press Ctrl-C in IDLE’s shell window or Ctrl-F6 to restart shell pip3 install flask==0.12 to overcome incompatibility with latest Flask version |
|---|
HTTP Requests and rounding
- Configure which paths are recognised to customise web server that Flask provides
- Each HTTP request starts with a request line specifying method & path + version of HTTP used
- Whether a HTTP request succeeds depends on whether path refers to a web document that is recognised by server & whether the method used is allowed for that web document
- E.g. HTTP server running on 127.0.0.1 & listening on port 5000; when visit http://127.0.0.1:5000/readme.txt, browser sends HTTP request of GET /readme.txt HTTP/1.1
- To web servers, HTTP paths are just strings, they do NOT refer to real files or folders
- Thus web servers can dynamically generate an HTML error page when we visit a URL that the server does not recognise
- To handle HTTP requests, can map specific paths to Python functions
- E.g. the path / can be mapped to a function home();
/readme.txt can be mapped to a function readme() - When a HTTP request is received, Flask examines the received path and looks for a mapping ⇒ routing
- Each HTTP request is routed to a Python function for processing based on the requested path and method used
- Route = each mapping of a path to a Python function
- If mapping found: Flask runs the associated Python function to generate a response
- Else: no mapping found, 404 (Not Found) status code produced
- E.g.

- Use decorators to declare a route and associate a path to a Python function
- Add decorations immediately before function’s definition ⇒ alter behaviour of a function without modifying its source code
- Start with an at-sign or “pie” symbol (@) followed by a decorator
- Use decorators generated using the route method of the main Flask object named app
- E.g.
# p03_simple_routes.pyWhen visit http://127.0.0.1:5000/readme.txt, website shows READ ME as the path /readme.txt is mapped to the function readme() by the decoration When visit http://127.0.0.1:5000/, website shows “Welcome” When visit http://127.0.0.1:5000/report, website shows “Everything is awesome When visit other URLs (e.g. http://127.0.0.1:5000/nothing), 404 (Not Found) error as no other paths have been mapped import flaskapp = flask.Flask(__name__)@app.route('/')def home():return 'Welcome'@app.route('/report')def generate_report():return 'Everything is awesome'@app.route('/readme.txt')def readme():return 'READ ME'if __name__ == '__main__':app.run() |
|---|
# p04_complex_routes.pyUses multiple decorators to declare a variety of routes import flaskapp = flask.Flask(__name__)@app.route('/')def index():return 'Routed to index()'@app.route('/css')def css():return 'Routed to css()'@app.route('/no_slash')def no_slash():return 'Routed to no_slash()'@app.route('/optional_slash/')def optional_slash():return 'Routed to optional_slash()'@app.route('/one/')@app.route('/one/two/')@app.route('/three/two/one')def multiple():return 'Routed to multiple()'@app.route('/string/<s>/')def string_variable(s):return 'Routed to string_variable(), s = {}'.format(s)@app.route('/integer/<int:i>/')def integer_variable(i):return 'Routed to integer_variable(), i = {}'.format(i)@app.route('/post_only/', methods=['POST'])def post_only():return 'Routed to post_only()'if __name__ == '__main__':app.run() |
|---|
- Fixed routes
- http://127.0.0.1:5000/: “Routed to index()”
- Indicates that your request was handled by the index() function
- Because the path component is just a slash character (/), which matches the route specified for index() function
- Note: routes are case sensitive
- E.g. http://127.0.0.1:5000/CSS: 404 Not Found error
- etc
- Trailing slashes
- Request’s path and route’s path must match exactly for the route to be chosen
- E.g. cannot have extra or less trailing slash (/) ⇒ 404 Not Found error
- But if Flask fails to find a route for a path that does NOT end with a slash, it will append a slash to the path and try again before giving up completely
- E.g. both http://127.0.0.1:5000/optional_slash (without a trailing slash) and http://127.0.0.1:5000/optional_slash/ (with a trailing slash) match the route for /optional_slash/ and will run the optional_slash() function
- BUT Flask does NOT automatically remove extra slash
- Multiple decorators
- Multiple paths can be routed to the same function
- E.g. the 3 decorations associate ALL of these URLs with the multiple() function:
- http://127.0.0.1:5000/one/
- http://127.0.0.1:5000/one/two/
- http://127.0.0.1:5000/three/two/one
- Variable Routes
- Flask routes can have variable parts, with a name surrounded by < and >
- Default: each variable part matches any non-empty sequence of characters that does not contain a slash (/)
- Route is matched if all variable parts in the path can be matched in this way ⇒ variable parts are extracted into str values and passed to the associate Python function as keyword arguments
- E.g. route specified by ‘/string/<s>/’, containing variable part numbed s
- This route matches any path starting with /string/ followed by >= 1 non-slash characters and an optional trailing function
- If match found: variable part is extracted into str value and passed to the function string_variable() as a keyword argument named s
- Else: Flask will try to find another route that matches, returning a 404 (Not Found) error if none can be found

- If e.g. http://127.0.0.1:5000/string/ or http://127.0.0.1:5000/string/hi/there: 404 Not Found
- Variable parts can also specify a converter using <converter:name> to modify the matching algorithm & convert the matched string to another type before being passed over to the function as a keyword argument
- E.g. ‘/integer/<int:i>’ with 1 variable part named i that uses the int converter ⇒ only digits are accepted ⇒ paths that start with /integer/ followed by >=1 digit(s) and an optional trialing slash will result in a match
- If match found: digit portion of the path is extracted & converted to int before being passed to integer_variable() as an int parameter
- Else: match fails, Flask tries to find another route that matches, returns 404 (Not Found) if no matching route can be found
- E.g. http://127.0.0.1:5000/integer/‐123 ⇒ 404 Not Found
- Routing by HTTP methods
- Each routes matches based on paths + can specify if it only applies to GET, POST, or both requests
- GET: retrieves data without making changes
- POST: submits or makes changes to server’s data permanently
- E.g. add, delete, update data on server
- To limit the HTTP methods accepted by a route, pass in a list of permitted HTTP methods as a keyword argument named methods in the decorator
- E.g. A route specified for /post_only is only accessible using POST
- If try to access http://127.0.0.1:5000/post_only using GET by directly entering it into address bar of a web browser, get 405 (Method Not Allowed) error
- To produce a POST request, need to reach the route by submitting a HTML form
- Generating paths from function names
- Routes provide a mapping from paths to Pyrgon functions
- But often need to go in opp direction: generate path for a given Python function
- Call url_for() function in flask nodule & pass it a string with the function’s name
- If path has any variables (e.g. s in “/string/<s>”), they should be provided as keyword arguments to url_for()
| E.g. p05_url_lookup.py Specify some routes & print 3 generated paths in the shell or command prompt window when the “root” site http://127.0.0.1:5000/ is visited import flaskfrom flask import url_forapp = flask.Flask(__name__)@app.route('/')def home():url1 = url_for('fixed_route')url2 = url_for('string-variable', s = 'example')url3 = url_for('integer_variable', i=2020)print(url1)print(url2)print(url3)return 'Check your shell or command prompt window'@app.route('/fixed/')def fixed_route():return 'Routed to fixed()'@app.route('/string/<s>')def string_variable(s):return 'Routed to string_variable(), s = {}'.format(s)@app.route('/integer/<int:i>')def integer_variable(i):return 'Routed to integer_variable(), i = {}'.format(i)if __name__ == '__main__':app.run() |
|---|
HTTP Responses and Status Codes
- Flask has been prepending various headers behind the scenes to produce valid HTTP responses
p06_hello_world.pyimport flaskapp = flask.Flask(__name__)@app.route('/')def index():return 'Hello, World!'if __name__ == '__main__':app.run() |
|---|
- Visit http://127.0.0.1:5000/, examine response headers using Chrome’s Developer Tools

- Together with the content, complete HTTP response sent to your browser, e.g.:
| HTTP/1.0 200 OK Content‐Type: text/html; charset=utf‐8 Content‐Length: 13 Server: Werkzeug/0.14.1 Python/3.7.0Date: Tue, 22 Jan 2019 08:01:52 GMT Hello, World! |
|---|
- Flask assumes output has a Content-Type of “text/html”
- ⇒ Flask expects our function to return a full HTML document and not just a plain string
- But most browsers will treat our string as a snippet of HTML intended for the document’s <body>
- E.g.

import flaskapp = flask.Flask(__name__)@app.route('/')def index():return '<h1>Welcome</h1> <b>Hello, </b> <i>World!</i>'if __name__ == '__main__':app.run() |
|---|
- But this is non-standard behaviour ⇒ our functions that are mapped to routes should really return full HTML documents
- Changing the Status Code
- Default: Flask assumes our responses have a HTTP status code of 200 (OK)
- Can override by returning a tuple instead of just a string
- Replacement HTTP status code should be provided as 2nd item of the tuple
import flask![]() app = flask.Flask(__name__)@app.route('/')def index():return ('', 500)if __name__ == '__main__':app.run() # When visit http://127.0.0.1:5000/,get HTTP ERROR 500 ⇒ Internal Server error |
|---|
- Changing the Response Headers
- Can also additional response headers by putting them into a Python dictionary & returning this dictionary as the 3rd item of our return tuple
- E.g. replace Content-Type header value with “text/plain” ⇒ for web browser to treat response as plain text instead of HTML
# p09_returning_plain_text.py![]() import flaskapp = flask.Flask(__name__)@app.route('/')def index():headers = {'Content-Type': 'text/plain'}return(‘<b>This is not HTML!</b>’, 200, headers) if __name__ == '__main__':app.run() # since text/plain not text/html, the string returned is no longer treated as HTML |
|---|
- Redirecting to another URL
- Besides overriding HTTP status code & response headers Flask also lets us generate a response that tells web browser to load a diff. URL instead ⇒ redirect
- Useful when location of a document has moved, or to let another Flask route take over the handling of a request
- Import redirect() function from flask module, and call it with the destination URL or path that as the 1st argument
- Then use the response generated by redirect() as the return value of the function
- E.g. redirect to an external site
# p10_redirect_example.pyimport flaskfrom flask import redirectapp = flask.Flask(__name__)@app.route('/')def index():return redirect('http://example.com')if __name__ == '__main__':app.run() |
|---|
- E.g. redirect user to another of our routes ⇒ use url_for() to look up correct path for redirect() based on the function that we want to reach (instead of hardcoding the redirected path as a string)
# p10_redirect_using_url_for.pyimport flaskfrom flask import redirect, url_forapp = flask.Flask(__name__)@app.route('/new_url/')def moved_index():return 'You have reached the new URL!'@app.route('/')def index():return redirect(url_for('moved_index'))if __name__ == '__main__':app.run() |
|---|

