Python Practical: Building a Lightweight Web Service with http.server

No dependencies, all in one: From debugging tools to a simple API framework!

๐ŸŒŸ This article teaches you how to create

  • ๐ŸŽฏ Custom Routing System: Supports different URL paths
  • ๐Ÿ—‚ Static File Service: Direct access to HTML, CSS, JS
  • ๐Ÿ“ฆ JSON API Interface: Supports GET / POST
  • ๐Ÿ“ Request Logging: Records access time, path, IP
  • โšก Easy to Extend: Can embed small automation or intranet tools

๐Ÿ— Project Structure

We prepare a simple directory:

project/
โ”œโ”€โ”€ server.py      # Core service
โ””โ”€โ”€ static/        # Static files
    โ””โ”€โ”€ index.html

<span>static/index.html</span> Example content:

&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;title&gt;Lightweight Server&lt;/title&gt;&lt;/head&gt;
&lt;body&gt;
  &lt;h1&gt;Welcome to Python http.server Practical&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;

๐Ÿ”ฅ Core Code

from http.server import BaseHTTPRequestHandler, HTTPServer
import json
import os
from urllib.parse import urlparse, parse_qs
from datetime import datetime

# Static file directory
STATIC_DIR = os.path.join(os.path.dirname(__file__), "static")

class MyWebServer(BaseHTTPRequestHandler):
    # General logging method
    def log_request_info(self):
        print(f"[{datetime.now()}] {self.client_address[0]} -&gt; {self.command} {self.path}")

    # Return JSON response
    def send_json(self, data, status=200):
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.end_headers()
        self.wfile.write(json.dumps(data, ensure_ascii=False).encode())

    # Return HTML file
    def send_static(self, filename):
        filepath = os.path.join(STATIC_DIR, filename)
        if os.path.exists(filepath):
            self.send_response(200)
            self.send_header("Content-Type", "text/html; charset=utf-8")
            self.end_headers()
            with open(filepath, "rb") as f:
                self.wfile.write(f.read())
        else:
            self.send_error(404, "File Not Found")

    # Handle GET requests
    def do_GET(self):
        self.log_request_info()
        parsed = urlparse(self.path)
        path, query = parsed.path, parse_qs(parsed.query)

        if path == "/":
            # Return homepage
            self.send_static("index.html")
        elif path == "/ping":
            # Return JSON
            self.send_json({"msg": "pong"})
        elif path == "/time":
            # Return current time
            self.send_json({"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")})
        else:
            self.send_error(404, "Not Found")

    # Handle POST requests
    def do_POST(self):
        self.log_request_info()
        parsed = urlparse(self.path)
        path = parsed.path

        content_length = int(self.headers.get('Content-Length', 0))
        body = self.rfile.read(content_length).decode()

        if path == "/echo":
            # Return original content
            self.send_json({"echo": body})
        else:
            self.send_error(404, "Not Found")


def run(server_class=HTTPServer, handler_class=MyWebServer, port=8000):
    server_address = ("0.0.0.0", port)
    httpd = server_class(server_address, handler_class)
    print(f"๐Ÿš€ Server started: http://127.0.0.1:{port}")
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        print("\n๐Ÿ›‘ Server stopped")
    finally:
        httpd.server_close()

if __name__ == "__main__":
    run()

๐Ÿงช Testing Effects

1. Open Homepage

Access via browser:

http://127.0.0.1:8000/

Displays <span>static/index.html</span> content.

2. Test <span>/ping</span> interface

curl http://127.0.0.1:8000/ping

Output:

{"msg": "pong"}

3. Get Server Time

curl http://127.0.0.1:8000/time

Output:

{"time": "2025-08-20 10:30:45"}

4. POST Echo

curl -X POST -d "Hello Server" http://127.0.0.1:8000/echo

Output:

{"echo": "Hello Server"}

๐Ÿ”ง Advanced Modification Suggestions

  1. Multi-threading Support

    from socketserver import ThreadingMixIn
    class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
        pass
    

    Replace <span>HTTPServer</span> with <span>ThreadingHTTPServer</span> to support simple multi-concurrency.

  2. Support JSON POST Check if <span>Content-Type</span> is <span>application/json</span> and parse automatically.

  3. Simple Routing Table Use a dictionary to map paths and methods for better extensibility:

    routes = {
        ('GET', '/ping'): handle_ping,
        ('POST', '/echo'): handle_echo
    }
    
  4. Intranet File Sharing Implement simple upload and download functionality with the <span>/upload</span> interface.

โš ๏ธ Notes

  • Insufficient Security: Suitable for local development and intranet testing, not recommended for direct exposure to the public network.
  • Limited Performance: Single-threaded version has weak concurrency capability, multi-threading or more professional frameworks are recommended for production.
  • Limited Protocol Support: Only supports basic HTTP/1.0 and 1.1.

๐Ÿง  Summary

Function Implementation Method
Static File Service <span>SimpleHTTPRequestHandler</span> or custom <span>send_static</span>
JSON API Interface <span>BaseHTTPRequestHandler</span> custom response
Routing Management Parse <span>path</span> or routing table
Multi-threading Support <span>ThreadingMixIn</span> extend concurrency
Logging <span>log_request_info</span> method records access

Leave a Comment