Сервер вызывает вас
Веб-сервер на Python (стандартный модуль http.server) строится из классов-обработчиков: создайте дочерний класс от BaseHTTPRequestHandler и напишите метод do_GET. Сервер принимает соединения и вызывает ваш метод один раз на каждый запрос — self.path и self.headers содержат запрос, а send_response, send_header и self.wfile формируют ответ.
Настоящий сервер вызывает serve_forever() на фиксированном порту и работает бесконечно. Примеры здесь выбирают порт 0 — случайный свободный порт — и запускают serve_forever в фоновом потоке, поэтому каждый пример сам запускает свой сервер, использует его и завершается. Они также переопределяют log_message пустым методом: родительский класс печатает строку журнала с отметкой времени для каждого запроса, а демонстрациям нужен чистый и стабильный вывод.
Первый обработчик
Самый маленький обработчик пишет одну строку текста. Всё, что делает API, — JSON, коды состояния, маршрутизация — происходит внутри метода такой же формы.
Привет из обработчика
Обработчик пишет в self.wfile; клиент читает ровно это как тело ответа, со строкой состояния, выбранной через send_response(200).
Вывод: полная программа на Python
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class HelloHandler(BaseHTTPRequestHandler):
# Сервер вызывает этот метод один раз на каждый GET-запрос.
def do_GET(self):
self.send_response(200)
self.send_header("Content-Type", "text/plain")
self.end_headers()
self.wfile.write("привет от сервера\n".encode())
def log_message(self, fmt, *args):
pass # без строк журнала запросов в выводе демонстрации
server = ThreadingHTTPServer(("127.0.0.1", 0), HelloHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
with urllib.request.urlopen(f"http://127.0.0.1:{port}/") as resp:
print("статус:", resp.status, resp.reason)
print("тело: ", resp.read().decode(), end="")
server.shutdown()
Ответ в формате JSON
Обработчик API делает три вещи: выбирает код состояния, устанавливает заголовок Content-Type и записывает закодированные данные — json.dumps создаёт текст, self.wfile.write отправляет байты.
Порядок важен: сначала send_response, затем заголовки, затем end_headers, и только потом тело.
JSON-эндпоинт
Обработчик записывает словарь прямо в ответ. Клиент получает корректный JSON и правильный тип содержимого — это основа каждого эндпоинта в остальных уроках этой группы.
Вывод: полная программа на Python
import json
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class UserHandler(BaseHTTPRequestHandler):
def do_GET(self):
body = json.dumps({"id": 1, "name": "Ana"}) + "\n"
self.send_response(200) # сначала статус
self.send_header("Content-Type", "application/json")
self.end_headers() # заголовки закрыты
self.wfile.write(body.encode()) # затем тело
def log_message(self, fmt, *args):
pass
server = ThreadingHTTPServer(("127.0.0.1", 0), UserHandler)
port = server.server_address[1]
threading.Thread(target=server.serve_forever, daemon=True).start()
with urllib.request.urlopen(f"http://127.0.0.1:{port}/user") as resp:
print("статус:", resp.status, resp.reason)
print("тип: ", resp.headers["Content-Type"])
print("тело: ", resp.read().decode(), end="")
server.shutdown()