GUI and Web Programming with Python (Tkinter/Flask)

Chapter 8
Web Foundations and the Flask Request Lifecycle

Diploma in Programming Languages (Java & Python)
GUI and Web Programming with Python (Tkinter/Flask)   •   Diploma

Web Foundations and the Flask Request Lifecycle

Introduction

Most web applications follow a client–server model. A browser or mobile application acts as a client, sends a request across a network, and receives a response from a server. Flask, a lightweight Python web framework, provides the tools needed to receive HTTP requests, execute application logic, access data, and construct responses.

Understanding this process is more important than memorising framework syntax. A Flask programmer must know how a URL is resolved, how DNS identifies a server, how HTTP methods express intentions, how status codes communicate outcomes, and how cookies maintain limited state between otherwise independent requests.

This chapter develops those foundations and then traces a request through a small Flask application.

Learning Objectives

By the end of this chapter, you should be able to:

  • explain client–server architecture and the role of a web server;
  • describe URLs, domain names, DNS resolution, and ports;
  • distinguish HTTP methods, status codes, headers, cookies, and request bodies;
  • explain the principal ideas of RESTful design;
  • create a basic Flask application with routes and responses;
  • read request data safely in Flask;
  • trace a request from browser to server and back again;
  • apply suitable security, validation, and error-handling practices.

1. The Client–Server Model

A client is a program that requests a service. Common clients include web browsers, mobile applications, command-line tools, and JavaScript code running in a browser. A server is a program that listens for requests, performs processing, and returns responses. The term server can also refer to the computer or virtual machine on which that program runs.

A typical interaction is:

  1. The user enters a URL or selects a link.
  2. The client identifies the destination server.
  3. The client opens a network connection and sends an HTTP request.
  4. The server routes the request to application code.
  5. The application validates input, performs business logic, and may access a database.
  6. The server returns an HTTP response.
  7. The client interprets the response and presents it to the user.

The model is request-driven: the server normally does not know a client’s state unless that state is sent with each request or stored using a mechanism such as cookies or server-side sessions. HTTP is also stateless by design. Each request is conceptually independent, even though an application can create the appearance of continuity.

In production, a Flask application may run behind a reverse proxy such as Nginx or Apache. The proxy can terminate HTTPS, serve static files, limit request sizes, and forward dynamic requests to a Python application server. Flask’s built-in development server is intended for development, not public production deployment.

2. URLs, Hosts, Ports, and DNS

A Uniform Resource Locator (URL) identifies a resource and provides instructions for reaching it. For example:

https://www.example.com:443/courses/python?level=diploma#overview

Its main components are:

ComponentExamplePurpose
SchemehttpsSpecifies the communication protocol
Hostnamewww.example.comIdentifies the destination host
Port443Identifies a service endpoint; often implied by the scheme
Path/courses/pythonIdentifies a resource or application route
Query stringlevel=diplomaSupplies parameters, usually for filtering or selection
FragmentoverviewIdentifies a client-side section; normally not sent to the server

A domain name is a human-readable name. Computers communicate using IP addresses, so the Domain Name System (DNS) translates names into addresses. DNS is distributed: recursive resolvers query authoritative name servers and cache results according to their time to live (TTL). A simplified resolution sequence is:

Browser → DNS resolver → authoritative DNS server → IP address
Browser → web server at that IP address → HTTP response

DNS does not itself deliver the web page. It only helps locate the host. After resolution, the client connects to a port. HTTP commonly uses port 80 and HTTPS commonly uses port 443, although any valid port may be used during development, such as Flask’s default port 5000.

HTTPS is HTTP carried over TLS. TLS provides encryption in transit, server authentication through certificates, and integrity protection. It does not automatically make an application secure: weak passwords, injection flaws, poor authorisation, and unsafe application logic remain possible.

3. HTTP Requests and Responses

HTTP messages consist of a start line, headers, a blank line, and optionally a body.

A request may look like this:

GET /products?category=books HTTP/1.1
Host: shop.example
Accept: text/html
User-Agent: ExampleBrowser/1.0

A response may look like this:

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 1280

<html>...</html>

Headers are metadata. The Host header identifies the requested host, Content-Type describes the representation being sent, Accept communicates formats the client can process, and Authorization can carry credentials or a token. Headers are not a substitute for access control; a server must still verify permissions.

HTTP methods

MethodTypical meaningCommon safety/idempotence property
GETRetrieve a representationSafe and normally idempotent
POSTSubmit data or request creation/actionNeither generally safe nor idempotent
PUTReplace a resource at a known URIIdempotent by definition of intended effect
PATCHPartially modify a resourceMay or may not be idempotent
DELETERemove a resourceIntended to be idempotent
HEADObtain headers without the response bodySafe and normally idempotent

A safe method is intended not to change server state. Idempotence means that repeating the same request has the same intended effect as performing it once, although logging, timestamps, or network effects may still occur. These properties guide caching, retries, and API design. They are semantic expectations, not permission to omit authentication or validation.

Status codes

Status codes are three-digit results grouped into classes:

ClassMeaningExamples
1xxInformational100 Continue
2xxSuccessful processing200 OK, 201 Created, 204 No Content
3xxRedirection or conditional response301 Moved Permanently, 304 Not Modified
4xxClient-side request problem400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 405 Method Not Allowed
5xxServer-side failure500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable

The distinction between 401 and 403 is important. 401 Unauthorized normally indicates that authentication is required or has failed, whereas 403 Forbidden indicates that the server understood the request but refuses to authorise it. A 404 response can mean that the resource does not exist; applications sometimes use it to avoid revealing whether a protected resource exists.

4. Cookies and Web State

A cookie is a small name–value item that a server asks a browser to store. The browser sends matching cookies in later requests. A response might include:

Set-Cookie: session_id=abc123; Secure; HttpOnly; SameSite=Lax

A later request might contain:

Cookie: session_id=abc123

The cookie does not necessarily contain the complete session data. It may contain an opaque identifier that refers to server-side session data. Flask’s signed-cookie session system stores session information in the cookie and signs it so that tampering can be detected; signing does not make the contents secret. Sensitive information should not be placed in a client-readable cookie unless its exposure is acceptable.

Important attributes include:

  • Secure: send the cookie only over HTTPS;
  • HttpOnly: prevent ordinary JavaScript from reading it;
  • SameSite: restrict cross-site sending and reduce some cross-site request forgery risks;
  • Max-Age or Expires: control lifetime;
  • Path and Domain: control where the cookie is sent.

Cookies require careful privacy and security management. Applications should minimise data collection, use consent mechanisms where required by applicable law, protect session identifiers, rotate or invalidate sessions when appropriate, and avoid placing passwords or secrets in cookies.

5. REST Principles

Representational State Transfer (REST) is an architectural style rather than a Python library or a rigid protocol. A REST-oriented service commonly applies these ideas:

  1. Resources: model meaningful entities such as users, courses, or orders.
  2. Identification: give resources stable identifiers, often URIs.
  3. Representations: transmit formats such as JSON rather than exposing internal objects directly.
  4. Uniform interface: use standard HTTP methods and status codes consistently.
  5. Stateless requests: each request contains the information needed to process it.
  6. Layered architecture: clients need not know whether a proxy, cache, or gateway lies between them and the application.
  7. Cacheability: responses may declare whether they can be reused.

For example, an API might use GET /api/courses/7 to retrieve course 7, POST /api/courses to create a course, PUT /api/courses/7 to replace it, and DELETE /api/courses/7 to remove it. A successful creation commonly returns 201 Created, potentially with a Location header.

REST does not require every URL to be a noun, nor does it prohibit actions. The central concern is consistent resource-oriented semantics. Real systems often combine REST-like endpoints with authentication services, asynchronous jobs, webhooks, and domain-specific operations.

6. Flask Application Structure

Flask is a Python framework based on Werkzeug for WSGI support and Jinja for templates. A minimal application is:

from flask import Flask

app = Flask(__name__)

@app.get("/")
def home():
    return "Welcome to the course site"

if __name__ == "__main__":
    app.run(debug=True)

The Flask object is the application instance. The route decorator associates a URL rule and HTTP method with a view function. When the development server receives GET /, Flask calls home, converts the returned value into a response, and sends it to the client.

debug=True is useful during development because it enables detailed error pages and reloading. It must not be enabled on a publicly accessible production server: the interactive debugger can disclose sensitive information and, if exposed, create severe security risks.

A more explicit response can return a body, status code, and headers:

from flask import Flask, jsonify, request

app = Flask(__name__)

@app.get("/api/greeting")
def greeting():
    name = request.args.get("name", "visitor")
    return jsonify(message=f"Hello, {name}!"), 200

@app.post("/api/courses")
def create_course():
    data = request.get_json(silent=True)
    if not isinstance(data, dict) or not data.get("title"):
        return jsonify(error="A title is required"), 400
    return jsonify(title=data["title"]), 201

request.args reads query-string values. JSON request data should be parsed with an appropriate content type and validated. A client-controlled value must never be trusted merely because it has the expected shape.

7. Tracing the Flask Request Lifecycle

Consider GET /api/greeting?name=Ravi sent to a local Flask server.

Stage 1: Name and connection

For a local URL such as http://127.0.0.1:5000/api/greeting?name=Ravi, no public DNS lookup is needed because 127.0.0.1 refers to the local machine. The browser opens a TCP connection to port 5000. With HTTPS, TLS negotiation would occur before HTTP data is exchanged.

Stage 2: HTTP request

The browser sends a request containing the method, path, query string, and headers. The fragment, if present in the URL, is handled by the browser and is not included in the HTTP request.

Stage 3: WSGI and Flask context

The web server passes the request environment to the Flask application through WSGI. Flask creates request and application contexts. The request context makes objects such as request available to the active view without passing them as explicit function arguments.

Stage 4: Routing

Flask compares the method and path with registered routes. The route /api/greeting matches GET; the query parameter name=Ravi is not part of the route pattern and is read from request.args. If no route matches, Flask returns 404. If the path matches but the method does not, Flask normally returns 405.

Stage 5: View execution

The view obtains the input, applies application logic, and returns a result. jsonify serialises a Python mapping as JSON and sets an appropriate content type. In a larger application, this stage may call services, repositories, and databases.

Stage 6: Response construction

Flask converts the return value to a response object. It sets a status code, headers, and body. Middleware or error handlers may modify the response before it leaves the application.

Stage 7: Client processing

The browser receives the response, interprets its status and content type, and displays or processes the body. If the response contains Set-Cookie, the browser applies the cookie rules before future requests.

A useful diagnostic technique is to inspect each stage separately: confirm DNS or host resolution, verify the port, inspect the request method and path, check route matching, log validated inputs, inspect the response status and headers, and finally examine client rendering.

8. Templates, Forms, and Static Files

Dynamic HTML is commonly generated with Jinja templates:

from flask import render_template

@app.get("/courses/<int:course_id>")
def course(course_id):
    return render_template("course.html", course_id=course_id)

The <int:course_id> converter ensures that the path segment is interpreted as an integer. Templates should escape untrusted output. Jinja’s normal HTML escaping helps prevent cross-site scripting when used correctly, but developers must still understand contexts such as HTML attributes, JavaScript, CSS, and URLs.

HTML forms often use POST for state-changing operations:

from flask import request, redirect, url_for

@app.post("/enrol")
def enrol():
    course_id = request.form.get("course_id", type=int)
    if course_id is None:
        return "Invalid course", 400
    # Validate authorisation and persist the enrolment here.
    return redirect(url_for("course", course_id=course_id), code=303)

A redirect after a successful form submission helps prevent accidental duplicate submissions when the user refreshes the result page. State-changing forms also require CSRF protection when cookie-based authentication is used.

9. Applied Case: Course Enrolment Service

This is an illustrative case. A training provider wants a small web service through which authenticated learners can view courses and enrol.

Background and problem

The initial prototype accepts a course identifier from a form and inserts an enrolment. Testing reveals three risks: invalid identifiers cause errors, users can enrol in courses they are not allowed to access, and refreshing the result page can repeat the submission.

Analysis

The endpoint should distinguish retrieval from modification. Course viewing is a GET; enrolment is a state-changing POST. Input must be validated before database use, and authorisation must be checked independently of authentication. A successful form submission should redirect.

Approach

A sound design uses:

  • GET /courses/<id> to retrieve a course;
  • POST /courses/<id>/enrol to request enrolment;
  • 400 Bad Request for malformed input;
  • 401 when the learner is not authenticated;
  • 403 when the learner lacks permission;
  • 404 when the course is absent or deliberately undisclosed;
  • 303 See Other to redirect after successful form processing.

The application validates types and business rules, uses parameterised database queries, protects the form against CSRF, and records an audit event without storing unnecessary personal data.

Outcome

The resulting service has clearer semantics, safer failure modes, and a predictable browser interaction. Users can refresh the redirected page without resubmitting the enrolment form. Administrators can distinguish malformed requests from permission failures and server faults.

Learning points

Framework convenience does not replace design. HTTP semantics, validation, authorisation, privacy, and error handling must be considered together. A route that works in a demonstration may still be unsafe in production.

10. Reliability, Security, and Professional Practice

Important practices include:

  • validate type, range, length, and business meaning of every external input;
  • use parameterised queries or a trusted ORM rather than constructing SQL strings;
  • authenticate users and authorise every protected operation;
  • use HTTPS in production and secure session-cookie attributes;
  • protect cookie-authenticated state-changing requests against CSRF;
  • avoid exposing stack traces, secrets, tokens, or personal data in errors and logs;
  • set suitable request-size and timeout limits;
  • return consistent error representations, especially for APIs;
  • separate configuration and secrets from source code;
  • test normal, invalid, unauthorised, missing, and unexpected cases;
  • use production WSGI deployment rather than Flask’s development server.

Observability is also part of professional operation. Logs should record correlation identifiers, routes, status codes, and timing while avoiding passwords, session values, and unnecessary personal information. Monitoring should distinguish client errors from application failures and infrastructure failures.

Contemporary applications frequently add JSON APIs, single-page clients, reverse proxies, containers, cloud deployment, and automated testing. These technologies do not remove the underlying lifecycle. They add layers that must still preserve correct HTTP semantics and secure boundaries.

Chapter Summary

The web is built around communication between clients and servers. URLs identify destinations and resources; DNS maps domain names to network addresses; ports identify service endpoints. HTTP requests contain methods, paths, headers, and sometimes bodies, while responses contain status codes, headers, and representations. Cookies provide a mechanism for carrying limited state between requests and must be protected through appropriate attributes and privacy practices.

REST encourages resource-oriented design, standard HTTP semantics, stateless requests, representations, and cacheability. Flask maps methods and paths to Python view functions. During the Flask lifecycle, a request is received through WSGI, matched by the router, processed by a view, converted into a response, and returned to the client. Reliable applications validate input, enforce authorisation, handle errors, use secure deployment practices, and test the complete request–response path.

Key Terms

Client: A program that sends requests to a service.

Server: A program or host that receives requests and provides services.

URL: An address identifying a resource and access method.

DNS: The distributed naming system that maps domain names to network addresses.

Host: The network destination named in a request.

Port: A numbered service endpoint on a host.

HTTP: The protocol used for web requests and responses.

Method: An HTTP operation such as GET, POST, PUT, PATCH, or DELETE.

Header: Metadata associated with an HTTP request or response.

Status code: A three-digit code describing the result of a request.

Cookie: A browser-stored value sent with matching later requests.

Session: State associated with a user or client across requests.

REST: An architectural style based on resources and a uniform interface.

Route: A Flask rule connecting a path and method to application code.

View function: A Python function that processes a matched Flask request.

WSGI: The Python interface between web servers and Python web applications.

JSON: A text representation frequently used for structured web data.

Authorisation: The process of deciding what an authenticated subject may do.

CSRF: A technique in which an unwanted state-changing request is induced from another site.

Reverse proxy: An intermediary that forwards client requests to backend services.

Review Questions

Short Answer Questions

  1. What is the difference between a client and a server?
  2. What information does DNS provide, and what does it not provide?
  3. Distinguish the purposes of an HTTP method and an HTTP status code.
  4. Why should a session cookie commonly use the HttpOnly and Secure attributes?
  5. What happens when Flask cannot find a route matching a request?

Long Answer Questions

  1. Explain the complete lifecycle of a browser request to a Flask route, from URL entry to response rendering.
  2. Compare GET, POST, PUT, PATCH, and DELETE, including their intended semantics and practical uses.
  3. Explain REST principles and evaluate how a course-enrolment API could apply them.
  4. Describe how cookies support sessions and analyse the main security and privacy risks.
  5. Explain why validation, authentication, authorisation, and error handling are separate responsibilities in a web application.

Analytical / Application Questions

  1. Design routes, methods, and status codes for an API that lists courses, retrieves one course, creates a course, and deletes a course. Justify each choice.
  2. A Flask endpoint accepts a course_id from a form and inserts a database record without checking the user’s role. Identify the risks and propose a safer request-processing sequence.
  3. A client receives 405 for POST /courses/4/enrol, although the path appears correct. Give at least three diagnostic steps that would help locate the problem.

Multiple Choice Questions with Answers

Question 1

Which component translates a domain name such as example.com into an IP address?

A. HTTP

B. DNS

C. TLS

D. Flask routing

Correct Answer: B. DNS

Question 2

Which HTTP method is normally intended for retrieving a resource without changing server state?

A. GET

B. POST

C. PATCH

D. DELETE

Correct Answer: A. GET

Question 3

What does HTTP status code 404 normally indicate?

A. The request succeeded and created a resource

B. Authentication is required

C. The requested resource or route was not found

D. The server is temporarily unavailable

Correct Answer: C. The requested resource or route was not found

Question 4

Which Flask object is commonly used to read query-string values?

A. session

B. request.args

C. request.form

D. url_for

Correct Answer: B. request.args

Question 5

What is the principal purpose of the HttpOnly cookie attribute?

A. To encrypt the cookie value

B. To prevent the cookie being sent over HTTPS

C. To restrict access to the cookie from ordinary client-side JavaScript

D. To make the cookie permanent

Correct Answer: C. To restrict access to the cookie from ordinary client-side JavaScript

Question 6

A Flask route matches the path but not the submitted HTTP method. Which response is normally appropriate?

A. 200

B. 201

C. 404

D. 405

Correct Answer: D. 405

Question 7

Which response status most appropriately indicates that a new resource was successfully created?

A. 201

B. 301

C. 401

D. 503

Correct Answer: A. 201

Question 8

Why is debug=True unsuitable for a publicly accessible production Flask server?

A. It disables all routing

B. It can expose detailed errors and an unsafe interactive debugger

C. It prevents Python functions from running

D. It forces every response to be XML

Correct Answer: B. It can expose detailed errors and an unsafe interactive debugger

Question 9

In a REST-oriented course API, which request is most appropriate for retrieving course 7?

A. POST /courses/7

B. GET /courses/7

C. DELETE /courses/7

D. PATCH /courses

Correct Answer: B. GET /courses/7

Question 10

What is the main reason for redirecting after successfully processing a browser form submission?

A. To convert POST automatically into a database transaction

B. To prevent DNS resolution

C. To reduce the likelihood that refreshing the result page repeats the submission

D. To hide all response headers

Correct Answer: C. To reduce the likelihood that refreshing the result page repeats the submission

GUI and Web Programming with Python (Tkinter/Flask)
Chapter 8 of 15