Building an HTTP server in Python might seem intimidating at first, but it’s actually pretty easy with the help of some built-in modules like `http.server`. In this tutorial, we’ll show you how to create a simple web server that can handle GET and POST requests using Python.
To start: let’s get our environment set up. You’ll need to have Python installed on your machine, which is pretty much a given if you’re reading this tutorial. If not, well…you might want to consider getting out more often!
Next, open up your favorite text editor (or IDE) and create a new file called `server.py`. This will be the main script that we’ll use to run our server.
Now let’s get down to business. We’re going to start by importing the necessary modules:
# This script imports the necessary modules for our server to run.
import http.server # This module allows us to create a basic HTTP server.
from urllib.parse import urlparse, parse_qs # This module allows us to parse URLs and query strings.
# This is a comment, it is used to explain the purpose of the code segment below.
# In this case, we are creating a new file called `server.py` to serve as our main script.
# We will use this file to run our server.
# This is a good practice to keep our code organized and easy to maintain.
# This is a comment, it is used to explain the purpose of the code segment below.
# Here, we are importing the necessary modules for our server to run.
# The `http.server` module allows us to create a basic HTTP server.
# The `urllib.parse` module allows us to parse URLs and query strings.
# We use the `from` keyword to specify which specific functions or classes we want to import from a module.
The `http.server` module provides us with all of the building blocks we need for creating an HTTP server in Python. The `urlparse` and `parse_qs` modules are used to help us handle URLs and query strings respectively.
Next, let’s define our request handler class:
# Import necessary modules
import http.server # Importing the http.server module to create an HTTP server
from urllib.parse import urlparse, parse_qs # Importing the urlparse and parse_qs modules to handle URLs and query strings respectively
# Define request handler class
class MyRequestHandler(http.server.BaseHTTPRequestHandler):
# Define function to handle GET requests
def do_GET(self):
try:
# Parse the query string from the URL
query = parse_qs(urlparse(self.path).query)
# Check if 'name' key is present in the query string
if 'name' in query['q']:
# Get the value of 'name' key
name = query['q'][0]
# Create a message with the name
message = f"Hello, {name}!"
else:
# If 'name' key is not present, display a default message
message = "Welcome to my web server!"
except Exception as e:
# If there is an error, send a 400 error response
self.send_error(400)
return
# Send a 200 success response
self.send_response(200)
# Set the content type header
self.send_header('Content-type', 'text/plain')
# End the headers
self.end_headers()
# Write the message to the response body
self.wfile.write(bytes(message, "utf-8"))
# Define function to handle POST requests
def do_POST(self):
# Get the content length from the request headers
content_length = int(self.headers['Content-Length'])
# Read the request body and decode it
body = self.rfile.read(content_length).decode()
try:
# Split the body into name and message
name, message = map(str.strip, body.split('&'))
# Check if 'name' and 'message' keys are present
if 'name' in name and 'message' in message:
# Get the values of 'name' and 'message' keys
name, message = name[len('name='):], message[len('message='):]
# Send a 201 success response
self.send_response(201)
# Set the content type header
self.send_header('Content-type', 'text/plain')
# End the headers
self.end_headers()
# Write a thank you message to the response body
self.wfile.write(bytes("Thank you for your feedback!", "utf-8"))
else:
# If 'name' or 'message' keys are missing, raise a ValueError
raise ValueError
except Exception as e:
# If there is an error, send a 400 error response
self.send_error(400)
return
In this example, we’re handling GET and POST requests differently. For GET requests, we’re parsing the query string to extract a name (if provided). If no name is provided, we default to a generic welcome message. We then send back an HTTP response with the appropriate headers and body.
For POST requests, we’re reading in the request body and parsing it into two variables: `name` and `message`. We check that both of these are present before sending back an HTTP response with a thank you message.
Now let’s add some code to our main script to start up our server:
# Importing necessary modules
import http.server # Importing the http.server module to handle HTTP requests
import socketserver # Importing the socketserver module to create a TCP server
import urllib.parse # Importing the urllib.parse module to parse URL strings
# Defining a custom request handler class
class MyRequestHandler(http.server.BaseHTTPRequestHandler): # Creating a class that inherits from the BaseHTTPRequestHandler class in the http.server module
def do_POST(self): # Defining a method to handle POST requests
content_length = int(self.headers['Content-Length']) # Getting the length of the request body from the Content-Length header
body = self.rfile.read(content_length) # Reading the request body
parsed_body = urllib.parse.parse_qs(body) # Parsing the request body into a dictionary
name = parsed_body[b'name'][0] # Getting the value of the 'name' key from the parsed body
message = parsed_body[b'message'][0] # Getting the value of the 'message' key from the parsed body
if name and message: # Checking if both name and message are present
self.send_response(200) # Sending a 200 OK response
self.send_header('Content-type', 'text/html') # Setting the content type header to 'text/html'
self.end_headers() # Ending the headers
self.wfile.write(b'<html><body><h1>Thank you for your message, ' + name + b'!</h1></body></html>') # Sending a thank you message with the name included
else: # If either name or message is missing
self.send_response(400) # Sending a 400 Bad Request response
self.send_header('Content-type', 'text/html') # Setting the content type header to 'text/html'
self.end_headers() # Ending the headers
self.wfile.write(b'<html><body><h1>Missing name or message.</h1></body></html>') # Sending an error message
# Starting up the server
if __name__ == '__main__': # Checking if the script is being run directly
Handler = MyRequestHandler # Assigning our custom request handler class to the Handler variable
http.server.test(Handler, ('', 80)) # Starting a test server on port 80 with our custom request handler
print("Server running at http://localhost:80/") # Printing a message to indicate that the server is running at http://localhost:80/
This code starts up our server on port 80 (the default HTTP port) and prints out a message to let us know that it’s running.
You now have your very own web server in Python, complete with GET and POST handling. It might not be the most sophisticated thing ever created, but hey…it works!
However, because http.server has certain limitations , you should remain cautious when using it to create a custom web framework. For example:
– The `BaseHTTPRequestHandler` class is designed for simple HTTP servers and doesn’t provide much flexibility or control over the server behavior. If you need more advanced features like session management, caching, or authentication, then you should consider using a full-fledged web framework like Flask or Django instead.
– The `test()` function is used to start up the server in test mode, which means that it will only run for one request and then exit immediately afterwards. If you need to keep your server running continuously, then you should use a different method (like starting the server with a separate process or daemon).