Django
This article starts from the underlying principles of web frameworks, covering HTTP protocol basics, the socket communication model, and how to evolve step by step from a hand-rolled framework to a WSGI-based Django framework. It helps you understand Django’s design philosophy and applicable scenarios.
The Essence of a Web Framework
We can think of it this way: every web application is essentially a socket server,
and the user's browser is a socket client. This means we can implement a web
framework ourselves.
Hand-rolled framework:
1. Socket code must be written by us
2. HTTP-formatted data must be handled manually (we can only extract the URL the user typed)
Based on the wsgiref module:
1. Encapsulates socket code for you
2. Parses HTTP-formatted data for you (provides a large dict)HTTP Protocol
HTTP (Hyper Text Transfer Protocol)
The protocol used to transfer hypertext from a WWW server to a local browser.
HTTP is an application-layer protocol consisting of requests and responses,
following a standard client-server model.
Features:
1. Supports the client/server model
2. Simple and fast
When a client requests a service from the server it only needs to send
the request method and path. Common request methods are GET, HEAD, and POST.
Because HTTP is simple, HTTP server programs are small and communication is fast.
3. Flexible
HTTP allows transmission of any type of data object.
4. Connectionless
Each connection handles only one request. After the server processes the client
request and sends the response, the connection is closed. This saves transmission time.
5. Stateless
HTTP is a stateless protocol — it has no memory of previous transactions.
The downside is that if later processing needs earlier information, it must be
retransmitted, increasing data volume per connection. The upside is that responses
are faster when prior context is not needed. Cookie and Session technology was
introduced to work around HTTP's statelessness.
Common request methods:
GET — Request the specified page and return the entity body
POST — Submit data to the specified resource; data is included in the request body
HEAD — Like GET, but returns only headers, no body
PUT — Replace the content of the specified document with data sent from the client
DELETE — Request the server to delete the specified page
CONNECT — Reserved in HTTP/1.1 for proxy servers that can switch to tunnel mode
OPTIONS — Allow the client to query server capabilities
TRACE — Echo the received request; mainly used for testing or diagnostics
HTTP working principle:
HTTP defines how a web client requests web pages from a web server and how the
server delivers them. HTTP uses a request/response model. The client sends a
request message containing the method, URL, protocol version, request headers,
and request data. The server replies with a status line that includes the protocol
version, a success or error code, server information, response headers, and
response data.
HTTP request/response steps:
1. Client connects to the web server
2. Client sends an HTTP request
3. Server accepts the request and returns an HTTP response
4. TCP connection is released
5. Client browser parses the HTML content
Status codes:
200 OK — Returned when the operation returns data in the response body.
204 No Content — Returned when the operation succeeds but returns no data.
304 Not Modified — Returned when testing whether an entity has been modified since last retrieval.
400 Bad Request — Returned when parameters are invalid.
403 Forbidden — Client error.
401 Unauthorized — Client error.
404 Not Found — Returned when the resource does not exist.
405 Method Not Allowed — Incorrect method/resource combination (e.g. DELETE on a collection).
412 Precondition Failed — Client error.
413 Payload Too Large — Returned when the request body is too large.
501 Not Implemented — Returned when the requested operation is not implemented.
503 Service Unavailable — Returned when the Web API service is unavailable.HTTP GET Request Format

HTTP Response Format

Custom Web Framework — Version 1
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('127.0.0.1', 8000))
sock.listen()
while True:
conn, addr = sock.accept()
data = conn.recv(8096)
# Add the response status line to the reply
conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
conn.send(b"OK")
conn.close()Custom Web Framework — Different Paths Return Different Content
"""
Return different content based on different URL paths
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # Bind IP and port
sk.listen() # Start listening
while 1:
# Wait for a connection
conn, add = sk.accept()
data = conn.recv(8096) # Receive message from client
# Extract the path from data
data = str(data, encoding="utf8") # Convert received bytes to string
# Split on \r\n
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url is the access path separated from the browser message
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # Must follow HTTP protocol, so the reply also needs a status line
# Return different content for different paths
if url == "/index/":
response = b"index"
elif url == "/home/":
response = b"home"
else:
response = b"404 not found!"
conn.send(response)
conn.close()Custom Web Framework — Different Paths Return Different Content (Function Version)
"""
Return different content based on different URL paths -- function version
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # Bind IP and port
sk.listen() # Start listening
# Encapsulate the different response parts into functions
def index(url):
s = "This is the {} page!".format(url)
return bytes(s, encoding="utf8")
def home(url):
s = "This is the {} page!".format(url)
return bytes(s, encoding="utf8")
while 1:
# Wait for a connection
conn, add = sk.accept()
data = conn.recv(8096) # Receive message from client
# Extract the path from data
data = str(data, encoding="utf8") # Convert received bytes to string
# Split on \r\n
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url is the access path separated from the browser message
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # Must follow HTTP protocol, so the reply also needs a status line
# Return different content for different paths; response is the specific response body
if url == "/index/":
response = index(url)
elif url == "/home/":
response = home(url)
else:
response = b"404 not found!"
conn.send(response)
conn.close()Custom Web Framework — Different Paths Return Different Content (Advanced Function Version)
"""
Return different content based on different URL paths -- advanced function version
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # Bind IP and port
sk.listen() # Start listening
# Encapsulate the different response parts into functions
def index(url):
s = "This is the {} page!".format(url)
return bytes(s, encoding="utf8")
def home(url):
s = "This is the {} page!".format(url)
return bytes(s, encoding="utf8")
# Define a mapping between URLs and the functions to execute
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# Wait for a connection
conn, add = sk.accept()
data = conn.recv(8096) # Receive message from client
# Extract the path from data
data = str(data, encoding="utf8") # Convert received bytes to string
# Split on \r\n
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url is the access path separated from the browser message
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # Must follow HTTP protocol, so the reply also needs a status line
# Return different content for different paths
func = None # Variable to hold the function to be executed
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# Send the specific response message
conn.send(response)
conn.close()Custom Web Framework — Returning a Web Page
"""
Return different content based on different URL paths -- advanced function version
Return standalone HTML pages
"""
import socket
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # Bind IP and port
sk.listen() # Start listening
# Encapsulate the different response parts into functions
def index(url):
# Read the content of the index.html page
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
# Return as bytes
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# Define a mapping between URLs and the functions to execute
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# Wait for a connection
conn, add = sk.accept()
data = conn.recv(8096) # Receive message from client
# Extract the path from data
data = str(data, encoding="utf8") # Convert received bytes to string
# Split on \r\n
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url is the access path separated from the browser message
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # Must follow HTTP protocol, so the reply also needs a status line
# Return different content for different paths
func = None # Variable to hold the function to be executed
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# Send the specific response message
conn.send(response)
conn.close()Custom Web Framework — Dynamic Web Pages
"""
Return different content based on different URL paths -- advanced function version
Return HTML pages
Make the web page dynamic
"""
import socket
import time
sk = socket.socket()
sk.bind(("127.0.0.1", 8080)) # Bind IP and port
sk.listen() # Start listening
# Encapsulate the different response parts into functions
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now) # Define a special placeholder in the page and replace it with dynamic data
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# Define a mapping between URLs and the functions to execute
list1 = [
("/index/", index),
("/home/", home),
]
while 1:
# Wait for a connection
conn, add = sk.accept()
data = conn.recv(8096) # Receive message from client
# Extract the path from data
data = str(data, encoding="utf8") # Convert received bytes to string
# Split on \r\n
data1 = data.split("\r\n")[0]
url = data1.split()[1] # url is the access path separated from the browser message
conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # Must follow HTTP protocol, so the reply also needs a status line
# Return different content for different paths
func = None # Variable to hold the function to be executed
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# Send the specific response message
conn.send(response)
conn.close()WSGI Module — Defining the Server
In a real Python web application, the system is generally split into two parts:
the server program and the application program.
The server program is responsible for wrapping the socket server and organizing all
the request data when a request arrives.
The application program handles the specific business logic. To make application
development easier, many web frameworks have emerged, such as Django, Flask, web.py,
etc. Different frameworks have different development styles, but all applications
ultimately need to work with a server program to serve users.
This means the server program needs to provide different support for different
frameworks — a chaotic situation that is bad for both sides. The server must support
all sorts of frameworks; a framework can only be used with servers that support it.
Standardization becomes critically important here. We can define a standard: if both
the server and the framework support this standard, they can work together. Once the
standard is set, each side implements it independently. This lets servers support
more frameworks, and frameworks run on more servers.
WSGI (Web Server Gateway Interface) is exactly such a specification. It defines the
interface format between Python web applications and web server programs, decoupling
the two sides.
Common WSGI servers include uwsgi and Gunicorn. The Python standard library provides
a standalone WSGI server called wsgiref, which Django's development server uses.Code
"""
Return different content based on different URL paths -- advanced function version
Return HTML pages
Make the web page dynamic
wsgiref module version
"""
import time
from wsgiref.simple_server import make_server
# Encapsulate the different response parts into functions
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now)
return bytes(s, encoding="utf8")
def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8")
# Define a mapping between URLs and the functions to execute
list1 = [
("/index/", index),
("/home/", home),
]
def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # Set HTTP response status code and headers
url = environ['PATH_INFO'] # Get the URL entered by the user
func = None
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
return [response, ]
if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8090, run_server)
print("Waiting for you on port 8090...")
httpd.serve_forever()Last updated on