راهنمای جامع برای توسعه دهندگان جهانی در مورد سفارشی سازی http.server پایتون (قبلاً BaseHTTPServer) برای ساخت APIهای ساده، سرورهای وب پویا و ابزارهای داخلی قدرتمند.
تسلط بر سرور HTTP داخلی پایتون: بررسی عمیق سفارشیسازی
پایتون به دلیل فلسفه "شامل همه چیز" خود مشهور است و یک کتابخانه استاندارد غنی را ارائه می دهد که به توسعه دهندگان این امکان را می دهد تا برنامه های کاربردی را با حداقل وابستگی خارجی بسازند. یکی از مفیدترین، اما اغلب نادیده گرفته شده، از این موارد، سرور HTTP داخلی است. چه آن را با نام مدرن Python 3، http.server
، یا نام قدیمی Python 2، BaseHTTPServer
، بشناسید، این ماژول دروازه ای برای درک پروتکل های وب و ساخت سرویس های وب سبک است.
در حالی که بسیاری از توسعه دهندگان برای اولین بار آن را به عنوان یک خط برای ارائه فایل ها در یک دایرکتوری می شناسند، قدرت واقعی آن در قابلیت توسعه آن نهفته است. با زیرکلاس کردن اجزای اصلی آن، می توانید این فایل سرور ساده را به یک برنامه وب سفارشی، یک API ساختگی برای توسعه فرانت اند، یک گیرنده داده برای دستگاه های IoT یا یک ابزار داخلی قدرتمند تبدیل کنید. این راهنما شما را از اصول اولیه به سفارشیسازی پیشرفته میبرد و شما را قادر میسازد تا از این ماژول فوقالعاده برای پروژههای خود استفاده کنید.
مبانی: یک سرور ساده از خط فرمان
قبل از پرداختن به کد، بیایید به رایج ترین مورد استفاده نگاهی بیندازیم. اگر پایتون را نصب کرده اید، از قبل یک سرور وب دارید. با استفاده از یک ترمینال یا خط فرمان به هر دایرکتوری در رایانه خود بروید و دستور زیر را اجرا کنید (برای پایتون 3):
python -m http.server 8000
بلافاصله، یک سرور وب دارید که روی پورت 8000 در حال اجرا است و فایل ها و زیرشاخه های مکان فعلی شما را ارائه می دهد. می توانید از طریق مرورگر خود در http://localhost:8000
به آن دسترسی پیدا کنید. این فوق العاده مفید است برای:
- به اشتراک گذاری سریع فایل ها از طریق یک شبکه محلی.
- تست پروژه های ساده HTML، CSS و جاوا اسکریپت بدون راه اندازی پیچیده.
- بررسی نحوه مدیریت درخواست های مختلف توسط یک سرور وب.
با این حال، این یک خط فقط نوک کوه یخ است. یک سرور عمومی از پیش ساخته شده را اجرا می کند. برای افزودن منطق سفارشی، مدیریت انواع درخواست های مختلف یا تولید محتوای پویا، باید اسکریپت پایتون خودمان را بنویسیم.
درک اجزای اصلی
یک سرور وب که با این ماژول ایجاد شده است از دو بخش اصلی تشکیل شده است: سرور و هندلر. درک نقشهای متمایز آنها کلید سفارشیسازی مؤثر است.
1. سرور: HTTPServer
وظیفه سرور گوش دادن به اتصالات شبکه ورودی در یک آدرس و پورت خاص است. این موتوری است که اتصالات TCP را می پذیرد و آنها را به یک هندلر منتقل می کند تا پردازش شوند. در ماژول http.server
، این معمولاً توسط کلاس HTTPServer
مدیریت می شود. شما با ارائه یک آدرس سرور (یک تاپل مانند ('localhost', 8000)
) و یک کلاس هندلر، یک نمونه از آن ایجاد می کنید.
مسئولیت اصلی آن مدیریت سوکت شبکه و سازماندهی چرخه درخواست-پاسخ است. برای اکثر سفارشی سازی ها، نیازی به تغییر کلاس HTTPServer
ندارید، اما ضروری است بدانید که آنجا است و نمایش را اجرا می کند.
2. هندلر: BaseHTTPRequestHandler
اینجاست که جادو اتفاق می افتد. هندلر مسئول تجزیه درخواست HTTP ورودی، درک آنچه مشتری درخواست می کند و تولید یک پاسخ HTTP مناسب است. هر بار که سرور یک درخواست جدید دریافت می کند، یک نمونه از کلاس هندلر شما برای پردازش آن ایجاد می کند.
ماژول http.server
چند هندلر از پیش ساخته شده را ارائه می دهد:
BaseHTTPRequestHandler
: این اساسی ترین هندلر است. درخواست و هدرها را تجزیه می کند اما نمی داند چگونه به روش های درخواست خاص مانند GET یا POST پاسخ دهد. این کلاس پایه عالی برای ارث بری است، زمانی که می خواهید همه چیز را از ابتدا بسازید.SimpleHTTPRequestHandler
: این ازBaseHTTPRequestHandler
ارث می برد و منطقی را برای ارائه فایل ها از دایرکتوری فعلی اضافه می کند. وقتیpython -m http.server
را اجرا می کنید، از این هندلر استفاده می کنید. اگر می خواهید منطق سفارشی را در بالای رفتار پیش فرض ارائه فایل اضافه کنید، نقطه شروع بسیار خوبی است.CGIHTTPRequestHandler
: اینSimpleHTTPRequestHandler
را گسترش می دهد تا اسکریپت های CGI را نیز مدیریت کند. این در توسعه وب مدرن کمتر رایج است اما بخشی از تاریخچه کتابخانه است.
تقریباً برای همه وظایف سفارشی سرور، کار شما شامل ایجاد یک کلاس جدید است که از BaseHTTPRequestHandler
یا SimpleHTTPRequestHandler
ارث می برد و متدهای آن را لغو می کند.
اولین سرور سفارشی شما: مثال "سلام، دنیا!"
بیایید از خط فرمان فراتر رفته و یک اسکریپت پایتون ساده برای یک سرور بنویسیم که با یک پیام سفارشی پاسخ می دهد. ما از BaseHTTPRequestHandler
ارث می بریم و متد do_GET
را پیاده سازی می کنیم که به طور خودکار برای مدیریت هرگونه درخواست HTTP GET فراخوانی می شود.
یک فایل به نام custom_server.py
ایجاد کنید:
# Use http.server for Python 3
from http.server import BaseHTTPRequestHandler, HTTPServer
import time
hostName = "localhost"
serverPort = 8080
class MyServer(BaseHTTPRequestHandler):
def do_GET(self):
# 1. Send the response status code
self.send_response(200)
# 2. Send headers
self.send_header("Content-type", "text/html")
self.end_headers()
# 3. Write the response body
self.wfile.write(bytes("<html><head><title>My Custom Server</title></head>", "utf-8"))
self.wfile.write(bytes("<p>Request: %s</p>" % self.path, "utf-8"))
self.wfile.write(bytes("<body>", "utf-8"))
self.wfile.write(bytes("<p>This is a custom server, created with Python's http.server.</p>", "utf-8"))
self.wfile.write(bytes("</body></html>", "utf-8"))
if __name__ == "__main__":
webServer = HTTPServer((hostName, serverPort), MyServer)
print(f"Server started http://{hostName}:{serverPort}")
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()
print("Server stopped.")
برای اجرای این، python custom_server.py
را در ترمینال خود اجرا کنید. وقتی از http://localhost:8080
در مرورگر خود بازدید می کنید، پیام HTML سفارشی خود را خواهید دید. اگر از یک مسیر متفاوت بازدید می کنید، مانند http://localhost:8080/some/path
، پیام آن مسیر را منعکس می کند.
بیایید متد do_GET
را تجزیه کنیم:
self.send_response(200)
: این خط وضعیت HTTP را ارسال می کند.200 OK
پاسخ استاندارد برای یک درخواست موفقیت آمیز است.self.send_header("Content-type", "text/html")
: این یک هدر HTTP ارسال می کند. در اینجا، به مرورگر می گوییم که محتوایی که ارسال می کنیم HTML است. این برای اینکه مرورگر صفحه را به درستی رندر کند، بسیار مهم است.self.end_headers()
: این یک خط خالی ارسال می کند و پایان هدرهای HTTP و آغاز بدنه پاسخ را نشان می دهد.self.wfile.write(...)
:self.wfile
یک شیء شبیه فایل است که می توانید بدنه پاسخ خود را در آن بنویسید. بایت ها را انتظار دارد، نه رشته ها، بنابراین ما باید رشته HTML خود را با استفاده ازbytes(" ...", "utf-8")
به بایت ها کدگذاری کنیم.
سفارشی سازی پیشرفته: دستور العمل های عملی
اکنون که اصول اولیه را درک می کنید، بیایید سفارشی سازی های قدرتمندتری را بررسی کنیم.
مدیریت درخواست های POST (do_POST
)
برنامه های کاربردی وب اغلب نیاز به دریافت داده ها دارند، به عنوان مثال، از یک فرم HTML یا یک تماس API. این معمولاً با یک درخواست POST انجام می شود. برای مدیریت این، متد do_POST
را لغو می کنید.
در داخل do_POST
، باید بدنه درخواست را بخوانید. طول این بدنه در هدر Content-Length
مشخص شده است.
در اینجا یک مثال از یک هندلر آورده شده است که داده های JSON را از یک درخواست POST می خواند و آن را دوباره بازتاب می دهد:
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
class APIServer(BaseHTTPRequestHandler):
def _send_cors_headers(self):
"""Sends headers to allow cross-origin requests"""
self.send_header("Access-Control-Allow-Origin", "*")
self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
self.send_header("Access-Control-Allow-Headers", "X-Requested-With, Content-Type")
def do_OPTIONS(self):
"""Handles pre-flight CORS requests"""
self.send_response(200)
self._send_cors_headers()
self.end_headers()
def do_POST(self):
# 1. Read the content-length header
content_length = int(self.headers['Content-Length'])
# 2. Read the request body
post_data = self.rfile.read(content_length)
# For demonstration, let's log the received data
print(f"Received POST data: {post_data.decode('utf-8')}")
# 3. Process the data (here, we just echo it back as JSON)
try:
received_json = json.loads(post_data)
response_data = {"status": "success", "received_data": received_json}
except json.JSONDecodeError:
self.send_response(400) # Bad Request
self.end_headers()
self.wfile.write(bytes('{"error": "Invalid JSON"}', "utf-8"))
return
# 4. Send a response
self.send_response(200)
self._send_cors_headers()
self.send_header("Content-type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(response_data).encode("utf-8"))
# Main execution block remains the same...
if __name__ == "__main__":
# ... (use the same HTTPServer setup as before, but with APIServer as the handler)
server_address = ('localhost', 8080)
httpd = HTTPServer(server_address, APIServer)
print('Starting server on port 8080...')
httpd.serve_forever()
نکته در مورد CORS: متد do_OPTIONS
و تابع _send_cors_headers
برای مدیریت اشتراکگذاری منابع متقابل (CORS) گنجانده شدهاند. اگر API خود را از یک صفحه وب که از مبدا (دامنه/پورت) متفاوتی ارائه میشود، فراخوانی میکنید، این اغلب ضروری است.
ساخت یک API ساده با پاسخ های JSON
بیایید مثال قبلی را گسترش دهیم تا یک سرور با مسیریابی اساسی ایجاد کنیم. ما می توانیم ویژگی self.path
را بررسی کنیم تا تعیین کنیم که مشتری چه منبعی را درخواست می کند و بر این اساس پاسخ دهیم. این به ما امکان می دهد چندین نقطه پایانی API را در یک سرور ایجاد کنیم.
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
# Mock data
users = {
1: {"name": "Alice", "country": "Canada"},
2: {"name": "Bob", "country": "Australia"}
}
class APIHandler(BaseHTTPRequestHandler):
def _set_headers(self, status_code=200):
self.send_response(status_code)
self.send_header("Content-type", "application/json")
self.send_header("Access-Control-Allow-Origin", "*")
self.end_headers()
def do_GET(self):
parsed_path = urlparse(self.path)
path = parsed_path.path
if path == "/api/users":
self._set_headers()
self.wfile.write(json.dumps(list(users.values())).encode("utf-8"))
elif path.startswith("/api/users/"):
try:
user_id = int(path.split('/')[-1])
user = users.get(user_id)
if user:
self._set_headers()
self.wfile.write(json.dumps(user).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "User not found"}).encode("utf-8"))
except ValueError:
self._set_headers(400)
self.wfile.write(json.dumps({"error": "Invalid user ID"}).encode("utf-8"))
else:
self._set_headers(404)
self.wfile.write(json.dumps({"error": "Not Found"}).encode("utf-8"))
# Main execution block as before, using APIHandler
# ...
با این هندلر، سرور شما اکنون یک سیستم مسیریابی ابتدایی دارد:
- یک درخواست GET به
/api/users
لیستی از همه کاربران را برمی گرداند. - یک درخواست GET به
/api/users/1
جزئیات آلیس را برمی گرداند. - هر مسیر دیگری منجر به خطای 404 یافت نشد می شود.
ارائه فایل ها و محتوای پویا با هم
اگر می خواهید یک API پویا داشته باشید اما همچنین فایل های استاتیک (مانند index.html
) را از همان سرور ارائه دهید، چه؟ سادهترین راه این است که از SimpleHTTPRequestHandler
ارث ببرید و وقتی یک درخواست با مسیرهای سفارشی شما مطابقت ندارد، به رفتار پیشفرض آن واگذار کنید.
تابع super()
بهترین دوست شما در اینجا است. این به شما امکان می دهد متد کلاس والد را فراخوانی کنید.
import json
from http.server import SimpleHTTPRequestHandler, HTTPServer
class HybridHandler(SimpleHTTPRequestHandler):
def do_GET(self):
if self.path == '/api/status':
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.end_headers()
response = {'status': 'ok', 'message': 'Server is running'}
self.wfile.write(json.dumps(response).encode('utf-8'))
else:
# For any other path, fall back to the default file-serving behavior
super().do_GET()
# Main execution block as before, using HybridHandler
# ...
اکنون، اگر یک فایل index.html
در همان دایرکتوری ایجاد کنید و این اسکریپت را اجرا کنید، بازدید از http://localhost:8080/
فایل HTML شما را ارائه می دهد، در حالی که بازدید از http://localhost:8080/api/status
پاسخ JSON سفارشی شما را برمی گرداند.
نکته ای در مورد پایتون 2 (BaseHTTPServer
)
در حالی که پایتون 2 دیگر پشتیبانی نمی شود، ممکن است با کد قدیمی مواجه شوید که از نسخه سرور HTTP آن استفاده می کند. مفاهیم یکسان هستند، اما نام ماژول ها متفاوت است. در اینجا یک راهنمای ترجمه سریع آورده شده است:
- Python 3:
http.server
-> Python 2:BaseHTTPServer
,SimpleHTTPServer
- Python 3:
socketserver
-> Python 2:SocketServer
- Python 3:
from http.server import BaseHTTPRequestHandler
-> Python 2:from BaseHTTPServer import BaseHTTPRequestHandler
نام متدها (do_GET
، do_POST
) و منطق اصلی یکسان باقی می مانند و انتقال اسکریپت های قدیمی به پایتون 3 را نسبتاً ساده می کند.
ملاحظات تولید: چه زمانی باید پیش رفت
سرور HTTP داخلی پایتون یک ابزار فوق العاده است، اما محدودیت های خود را دارد. درک اینکه چه زمانی انتخاب درستی است و چه زمانی باید به دنبال راه حل قوی تری باشید، بسیار مهم است.
1. همزمانی و عملکرد
به طور پیش فرض، HTTPServer
تک رشته ای است و درخواست ها را به ترتیب پردازش می کند. اگر پردازش یک درخواست مدت زیادی طول بکشد، تمام درخواست های ورودی دیگر را مسدود می کند. برای موارد استفاده کمی پیشرفته تر، می توانید از socketserver.ThreadingMixIn
برای ایجاد یک سرور چند رشته ای استفاده کنید:
from socketserver import ThreadingMixIn
from http.server import HTTPServer
class ThreadingHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
pass
# In your main block, use this instead of HTTPServer:
# webServer = ThreadingHTTPServer((hostName, serverPort), MyServer)
در حالی که این به همزمانی کمک می کند، اما هنوز برای محیط های تولید با عملکرد بالا و ترافیک بالا طراحی نشده است. چارچوب های وب و سرورهای برنامه کاربردی کامل (مانند Gunicorn یا Uvicorn) برای عملکرد، مدیریت منابع و مقیاس پذیری بهینه شده اند.
2. امنیت
http.server
با تمرکز اصلی بر امنیت ساخته نشده است. فاقد محافظت های داخلی در برابر آسیب پذیری های رایج وب مانند اسکریپت نویسی متقابل (XSS)، جعل درخواست متقابل سایت (CSRF) یا تزریق SQL است. چارچوب های درجه تولید مانند Django، Flask و FastAPI این محافظت ها را به طور پیش فرض ارائه می دهند.
3. ویژگی ها و انتزاع
با رشد برنامه شما، ویژگی هایی مانند ادغام پایگاه داده (ORMs)، موتورهای قالب، مسیریابی پیچیده، احراز هویت کاربر و میان افزار را می خواهید. در حالی که می توانید همه اینها را خودتان در بالای http.server
بسازید، اساساً در حال اختراع مجدد یک چارچوب وب هستید. چارچوب هایی مانند Flask، Django و FastAPI این اجزا را به روشی ساختاریافته، آزمایش شده و قابل نگهداری ارائه می دهند.
از http.server
برای:
- یادگیری و درک HTTP.
- نمونه سازی سریع و اثبات مفاهیم.
- ساخت ابزارها یا داشبوردهای ساده و فقط داخلی.
- ایجاد سرورهای API ساختگی برای توسعه فرانت اند.
- نقاط پایانی جمع آوری داده های سبک برای IoT یا اسکریپت ها.
به یک چارچوب برای: بروید
- برنامه های کاربردی وب رو به عموم.
- APIهای پیچیده با احراز هویت و تعاملات پایگاه داده.
- برنامه هایی که در آن امنیت، عملکرد و مقیاس پذیری حیاتی هستند.
نتیجه گیری: قدرت سادگی و کنترل
http.server
پایتون گواهی بر طراحی کاربردی این زبان است. این یک پایه ساده و در عین حال قدرتمند برای هر کسی که نیاز به کار با پروتکل های وب دارد، فراهم می کند. با یادگیری نحوه سفارشی کردن هندلرهای درخواست آن، کنترل دقیقی بر چرخه درخواست-پاسخ به دست میآورید و شما را قادر میسازد تا طیف گستردهای از ابزارهای مفید را بدون سربار یک چارچوب وب کامل بسازید.
دفعه بعد که به یک سرویس وب سریع، یک API ساختگی نیاز داشتید یا فقط می خواهید HTTP را آزمایش کنید، این ماژول همه کاره را به خاطر بسپارید. این چیزی بیش از یک فایل سرور است. این یک بوم خالی برای خلاقیت های مبتنی بر وب شما است که درست در کتابخانه استاندارد پایتون گنجانده شده است.