Один обработчик на маршрут
Маршрутизация решает, какой код отвечает на какой запрос. В стандартном http.server нет встроенного маршрутизатора — и для обычных случаев он не нужен: do_GET смотрит на self.path и выбирает нужную ветку.
Параметр пути — это часть URL, которая является данными: в /users/7 это 7. Если разбить путь на сегменты, вы получите его: "/users/7".strip("/").split("/") даёт ['users', '7']. Фреймворки добавляют поверх этого маршрутизаторы по шаблонам, но внутри они делают именно это.
Маршруты и значения пути
Два маршрута: список по адресу /users и один пользователь по адресу /users/7. Сегмент с id — это параметр пути, часть URL, которая несёт данные.
Маршрут с параметром пути
Один и тот же do_GET отправляет /users в одну ветку, а /users/7 — в другую: одно разбиение, и второй сегмент даёт 7.
Вывод: полная программа на Python
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class UsersHandler(BaseHTTPRequestHandler):
def do_GET(self):
parts = self.path.strip("/").split("/") # "/users/7" -> ["users", "7"]
if parts == ["users"]:
self.reply("все пользователи: Ana, Ben, Cleo\n")
elif len(parts) == 2 and parts[0] == "users":
user_id = parts[1] # параметр пути
self.reply(f"один пользователь, id = {user_id}\n")
else:
self.reply("здесь ничего нет\n", status=404)
def reply(self, text, status=200):
self.send_response(status)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(text.encode())
def log_message(self, fmt, *args):
pass
server = ThreadingHTTPServer(("127.0.0.1", 0), UsersHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
for path in ["/users", "/users/7"]:
with urllib.request.urlopen(f"http://127.0.0.1:{port}{path}") as resp:
print("GET", path, "->", resp.read().decode(), end="")
server.shutdown()
Параметры запроса
Часть после ? содержит параметры запроса — настройки запроса, например текст поиска или лимит: /search?q=python&limit=2. urlparse отделяет их от пути, а parse_qs превращает их в словарь списков — значения всегда строки, а .get обрабатывает отсутствующие.
Простое правило: путь говорит, какой ресурс, а запрос — как: фильтры, сортировка, постраничный вывод.
Чтение строки запроса
Обработчик читает q и limit из строки запроса. Во втором запросе limit отсутствует, поэтому обработчик использует значение по умолчанию — отсутствующие параметры — это нормально, учитывайте их заранее.
Вывод: полная программа на Python
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
from urllib.parse import parse_qs, urlparse
class SearchHandler(BaseHTTPRequestHandler):
def do_GET(self):
url = urlparse(self.path) # разделяет путь и запрос
params = parse_qs(url.query) # {"q": ["python"], ...}
q = params.get("q", [""])[0]
limit = params.get("limit", ["10"])[0] # значение по умолчанию для отсутствующих
body = f"поиск: {q}, лимит: {limit}\n"
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write(body.encode())
def log_message(self, fmt, *args):
pass
server = ThreadingHTTPServer(("127.0.0.1", 0), SearchHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
for path in ["/search?q=python&limit=2", "/search?q=json"]:
with urllib.request.urlopen(f"http://127.0.0.1:{port}{path}") as resp:
print("GET", path, "->", resp.read().decode(), end="")
server.shutdown()