37. تمرین عملی: نسخه ۱۷

این نسخهٔ جدید تغییرات زیر را معرفی میکند:
- این نسخه به سرور آپاچی/ویندوز منتقل خواهد شد؛
- برای انجام این انتقال، نسخهٔ ۱۷ تمام وابستگیهای مورد نیاز را در پوشهٔ [impots / http-servers/ 12] خود دارد. لطفاً توجه داشته باشید که نسخههای قبلی وابستگیهای خود را از پوشههای مختلف در سراسر پروژهٔ [python-flask-2020] دریافت میکردند؛
37.1. جابجایی وابستگیهای برنامه

لطفاً توجه داشته باشید که وابستگیهای برنامه در اسکریپت [syspath] مدیریت میشوند. در نسخه قبلی، این اسکریپت به شرح زیر بود:
def configure(config: dict) -> dict:
import os
# دایرکتوری این فایل
script_dir = os.path.dirname(os.path.abspath(__file__))
# مسیر ریشه
root_dir = "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020"
# وابستگیها
absolute_dependencies = [
# پوشههای پروژه
# BaseEntity, MyException
f"{root_dir}/classes/02/entities",
# InterfaceImpôtsDao, InterfaceImpôtsMétier, InterfaceImpôtsUi
f"{root_dir}/impots/v04/interfaces",
# AbstractImpôtsdao, ImpôtsConsole, ImpôtsMétier
f"{root_dir}/impots/v04/services",
#ImpotsDaoWithAdminDataInDatabase
f"{root_dir}/impots/v05/services",
#AdminData، ImpôtsError، TaxPayer
f"{root_dir}/impots/v04/entities",
# ثوابت، بازهها
f"{root_dir}/impots/v05/entities",
# لاگر، SendAdminMail
f"{root_dir}/impots/http-servers/02/utilities",
# پوشهٔ اصلی اسکریپت
script_dir,
# پیکربندیها [database, layers, parameters, controllers, views]
f"{script_dir}/../configs",
#کنترلرها
f"{script_dir}/../controllers",
#پاسخها HTTP
f"{script_dir}/../responses",
# قالبهای نما
f"{script_dir}/../models_for_views",
]
# تنظیم syspath
from myutils import set_syspath
set_syspath(absolute_dependencies)
# ذخیره پیکربندی
return {
"root_dir": root_dir,
"script_dir": script_dir
}
تمام وابستگیهایی که نام مطلقشان به متغیر [root_dir] در خط ۸ وابسته است، باید جابجا شوند، یعنی خطوط ۱۳–۲۶.
اسکریپت [syspath] در نسخه جدید به شرح زیر خواهد بود:
def configure(config: dict) -> dict:
import os
# دایرکتوری این فایل
script_dir = os.path.dirname(os.path.abspath(__file__))
#وابستگیها
absolute_dependencies = [
# واحدهای برنامه
f"{script_dir}/../entities",
#لایه [dao]
f"{script_dir}/../layers/dao",
# لایه [métier]
f"{script_dir}/../layers/métier",
# خدمات
f"{script_dir}/../utilities",
# پوشهٔ اصلی اسکریپت
script_dir,
#پیکربندیها [database, layers, parameters, controllers, views]
f"{script_dir}/../configs",
#کنترلرها
f"{script_dir}/../controllers",
#پاسخها HTTP
f"{script_dir}/../responses",
# قالبهای نما
f"{script_dir}/../models_for_views",
]
# مسیر سیستم را تنظیم کنید
import sys
# افزودن وابستگیهای مطلق پروژه
for directory in absolute_dependencies:
#بررسی میکند که آیا پوشه وجود دارد یا خیر
existe = os.path.exists(directory) and os.path.isdir(directory)
if not existe:
# به توسعهدهنده اطلاع دهید
raise BaseException(f"[set_syspath] le dossier du Python Path [{directory}] n'existe pas")
else:
# قرار دادن پوشه در ابتدای syspath
sys.path.insert(0, directory)
#پیکربندی اعمال میشود
return {
"script_dir": script_dir,
}
- خطوط ۸–۲۷: تمام وابستگیها اکنون نسبت به متغیر [script_dir] در خط ۵ هستند؛
- خطوط ۴۲–۴۵: متغیر [root_dir] از پیکربندی syspath حذف شده است؛
- خط ۱۰: اجزای برنامه در پوشه [entities] [1] قرار دارند؛
- خط ۱۲: لایه [dao] در پوشه [layers/dao] [2] قرار دارد؛
- خط ۱۴: لایه [métier] در پوشه [layers/métier] [2] قرار دارد؛
- خط ۱۶: ابزارهای [Logger, SendMail] در پوشه [utilities] [3] قرار دارند؛
- خطوط ۲۹–۴۰: مسیر پایتون برنامه بدون وارد کردن ماژول [myutils] محاسبه میشود؛

37.2. آزمایشها
در این مرحله، نسخهٔ ۱۷ باید کار کند. لطفاً این را بررسی کنید.
37.3. مهاجرت یک برنامه پایتون/فلسک به یک سرور آپاچی/ویندوز
37.3.1. منابع
برای انتقال یک برنامه Flask به Apache / Windows، مجبور شدم در اینترنت جستجو کنم. این لینکی است که به من در شروع کار کمک کرد: [https://medium.com/@madumalt/flask-app-deployment-in-windows-apache-server-mod-wsgi-82e1cfeeb2ed];
من از اطلاعات این لینک استفاده کردم، به جز پیکربندی سرور آپاچی. برای آن، از یک پیکربندی نمونه سرور آپاچی از Laragon استفاده کردم.
37.3.2. نصب ماژول پایتون mod_wsgi
اپلیکیشن پایتون/فلسک که ما توسعه دادیم از سرور WSGI (رابط دروازه وب سرور) که همراه با فلسک عرضه میشود، یعنی [werkzeug]، استفاده میکرد. این سرور در |اینجا| توضیح داده شده است. این لینک نحوه کار سرورهای WSGI را توضیح میدهد. سرورهای |WSGI| مختلفی وجود دارند. یکی از اینها سرور آپاچی است که در حالت WSGI اجرا میشود. این راهحلی است که در اینجا به کار گرفته شده است، زیرا لارگون (Laragon) که ما نصب کردهایم، همراه با یک سرور آپاچی عرضه میشود.
برای اینکه سرور آپاچی بتواند یک برنامه پایتون را میزبانی کند، باید ماژول پایتون [mod_wsgi] را نصب کنیم. نصب این ماژول دشوار است زیرا در طول فرآیند نصب، کامپایل C++ انجام میشود. برای تکمیل موفقیتآمیز نصب، به یک کامپایلر C++ مایکروسافت نیاز است. یک راهحل ساده نصب نسخه فعلی Visual Studio Community [https://visualstudio.microsoft.com/fr/vs/community/] است.
اگر برای هیچ منظوری به جز [mod_wsgi] به ویژوال استودیو نیاز ندارید، میتوانید نصب را به محیط C++ محدود کنید:

پس از نصب کامپایلر C++، ماژول [mod_wsgi] را میتوان در یک ترمینال با استفاده از دستور PyCharm نصب کرد:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>SET MOD_WSGI_APACHE_ROOTDIR=C:\MyPrograms\laragon\bin\apache\httpd-2.4.35-win64-VC15
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>pip install mod_wsgi
Collecting mod_wsgi
Using cached mod_wsgi-4.7.1.tar.gz (498 kB)
Using legacy setup.py install for mod-wsgi, since package 'wheel' is not installed.
Installing collected packages: mod-wsgi
Running setup.py install for mod-wsgi ... done
Successfully installed mod-wsgi-4.7.1
- خط ۱: مقدار متغیر محیطی [MOD_WSGI_APACHE_ROOTDIR] را تنظیم کنید. این مقدار، محل قرارگیری سرور آپاچی در سیستم فایل است. در اینجا، این مکان [<laragon>\bin\apache\httpd-2.4.35-win64-VC15] است، که در آن <laragon> پوشه نصب Laragon است. میتوانید این مسیر را به روشهای مختلف به دست آورید. در اینجا یکی از روشها که با استفاده از یکی از گزینههای Laragon به دست آمده است، آورده شده است:

در [1-3]، فایل [httpd.conf] فایل اصلی پیکربندی سرور آپاچی است. این فایل سپس در یک ویرایشگر متن (در مثال زیر Notepad++) باز میشود:

در [2]، مسیر نصب آپاچی بخشی است که قبل از عبارت [conf\httpd.conf] قرار دارد.
بیایید به نصب ماژول [mod_wsgi] بازگردیم:
- خطوط ۳–۹: نصب ماژول [mod_wsgi]؛
37.3.3. پیکربندی سرور لارگون آپاچی
ما قصد داریم سرور لارگون آپاچی را پیکربندی کنیم. با فایل پیکربندی اصلی آن، [httpd.conf]، شروع میکنیم:

به انتهای فایل [httpd.conf] میرویم:
…
IncludeOptional "C:/MyPrograms/laragon/etc/apache2/alias/*.conf"
IncludeOptional "C:/MyPrograms/laragon/etc/apache2/sites-enabled/*.conf"
Include "C:/MyPrograms/laragon/etc/apache2/httpd-ssl.conf"
Include "C:/MyPrograms/laragon/etc/apache2/mod_php.conf"
# پایتون mod_wsgi
LoadModule wsgi_module "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
- خط ۸ در انتهای فایل موجود [httpd.conf] اضافه شده است. این خط به سرور آپاچی میگوید که یک جزء از ماژول [mod_wsgi] را که به تازگی نصب کردهایم، کجا پیدا کند؛
یک روش ساده برای یافتن مسیر خط ۸ این است که دستور زیر را در یک ترمینال اجرا کنید: PyCharm:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\impots\http-servers\11>mod_wsgi-express module-config
LoadModule wsgi_module "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
WSGIPythonHome "c:/data/st-2020/dev/python/cours-2020/python3-flask-2020/venv"
برخی مستندات بیان میکنند که خطوط ۲ و ۳ باید به انتهای فایل [httpd.conf] اضافه شوند. در مورد من، خط ۳ فوق باعث خطا شد (ماژول [encodings] موجود نیست). بنابراین، خط ۳ بالا در فایل [httpd.conf] گنجانده نشد. فقط خط ۲ گنجانده شد. معنی پارامترهای مختلف ماژول [mod_wsgi] که میتوان در فایلهای پیکربندی آپاچی استفاده کرد، در |اینجا| توضیح داده شده است.
سپس، پروتکل HTTPS را روی سرور آپاچی فعال خواهیم کرد:

- در [1-4]، پروتکل HTTPS را روی سرور آپاچی فعال میکنیم؛
اکنون میتوانیم از URL و [https://serveur/chemin] استفاده کنیم؛
برای پیکربندی آپاچی جهت ارائه یک برنامه Flask، لینک |بالا| از میزبانهای مجازی استفاده میکند. Laragon همچنین گزینه مدیریت میزبانهای مجازی را ارائه میدهد:

- در [1-3]، ما به Laragon دستور میدهیم تا میزبانهای مجازی را بهطور خودکار ایجاد کند؛
مرحله بعدی ایجاد یک پروژه وب با Laragon است:
![]() |
![]() | ![]() |
- در [1-3]، یک پروژه خالی PHP ایجاد کنید؛
- در [4-8]، Laragon یک سایت مجازی به نام [auto.projet-test.test] ایجاد کرده است که توسط فایلهای [auto.projet-test.test.conf] و [8] در پوشه [sites-enabled] و [7] پیکربندی شده است. این پوشه در آدرس [<laragon>\etc\apache2\sites-enabled] قرار دارد، که در آن [laragon] پوشه نصب Laragon است؛
اگرچه این بخشی از کاری که در حال حاضر روی آن کار میکنیم نیست، ممکن است بخواهید نگاهی به وبسایت [projet-test] که به تازگی ایجاد کردهایم بیندازید:

- در [1-5]، یک پروژه خالی ایجاد شده است. این یک پروژه PHP است که در پوشه [<laragon>/www] قرار دارد، جایی که [laragon] پوشه نصب Laragon است؛
اکنون بیایید فایل [auto.projet-test.test.conf] تولید شده توسط Laragon را در پوشه [<laragon>\etc\apache2\sites-enabled] بررسی کنیم:
define ROOT "C:/MyPrograms/laragon/www/projet-test/"
define SITE "projet-test.test"
<VirtualHost *:80>
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
<VirtualHost *:443>
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile C:/MyPrograms/laragon/etc/ssl/laragon.crt
SSLCertificateKeyFile C:/MyPrograms/laragon/etc/ssl/laragon.key
</VirtualHost>
- خط ۱: ریشه پروژه ایجادشده در سیستم فایل؛
- خط ۲: نام سرور مجازی. فایلهای URL برای این سرور به شکل [http(s)://projet-test.test/chemin] خواهند بود؛
- خطوط ۴–۱۲: پیکربندی سایت مجازی برای پورت ۸۰ (خط ۴) و پروتکل HTTP؛
- خطوط 14–27: پیکربندی سایت مجازی برای پورت 443 (خط 14) و پروتکل HTTPS؛
بیایید ببینیم یک سرور مجازی چگونه کار میکند. ابتدا، بیایید سرور آپاچی و پروتکل PHP را راهاندازی کنیم:
سپس با استفاده از یک مرورگر، URL [http://projet-test.test/] را درخواست میکنیم:

- در [1]، URL درخواستشده؛
- در [2]، از پروتکل HTTP استفاده شد؛
- در [3]، از آنجا که پروژه [projet-test] خالی است، ما شاخص پوشه آن (فهرست محتویات آن) را به دست میآوریم که خالی است؛
اکنون بیایید URL امن را از [https://projet-test.test/] درخواست کنیم:

- در [1-2]، همان پاسخ قبلی را دریافت میکنیم اما با پروتکل HTTPS [1]؛
ایجاد سرور مجازی [projet-test.test] یک ورودی جدید در فایل [<windows>/system32/drivers/etc/hosts] ایجاد کرد:

- خط ۲۳: آدرس IP برای نام [projet-test.test] برابر با 127.0.0.1 است، یعنی آدرس [localhost] (خط ۲۰)، که همان ماشین محلی است. بنابراین وقتی URL [http://projet-test.test/chemin] را در یک مرورگر تایپ میکنید، درخواست به آدرس 127.0.0.1 روی پورت 80 ارسال میشود. سپس سرور آپاچی روی ماشین محلی (localhost) پاسخ میدهد.
ممکن است این سؤال پیش بیاید که چرا وقتی درخواست «[http://projet-test.test/]» وارد میشود، سرور آپاچی از پیکربندی فایل «[<laragon>\etc\apache2\sites-enabled\auto.projet-test.test.conf]» استفاده میکند:

برای درک این موضوع، باید ببینیم مرورگر هنگام ارسال این درخواست چه چیزی را به سرور آپاچی میفرستد. بیایید این درخواست را با استفاده از Postman ارسال کنیم:

- در [1-3]، ما درخواستی برای HTTPS و [1] ارسال میکنیم؛
- برای [4]، Postman نشان میدهد که گواهی امنیتی را شناسایی نکرده است. پروتکل HTTPS یک اتصال رمزگذاریشده بین کلاینت وب (در این مورد، Postman) و سرور آپاچی برقرار میکند. این اتصال رمزگذاریشده با استفاده از گواهینامههایی که بین کلاینت و سرور مبادله میشوند، برقرار میگردد. این سرور است که با ارسال یک گواهینامه امنیتی به کلاینت، فرآیند برقراری اتصال رمزگذاریشده را آغاز میکند. برای اینکه این گواهی توسط کلاینت پذیرفته شود، باید امضا شده باشد – به عبارت دیگر، از سازمانهایی که مجاز به صدور گواهیهای امنیتی هستند خریداری شده باشد. هنگامی که ما پروتکل HTTPS لارگون را فعال کردیم، لارگون خود گواهی امنیتی را ایجاد کرد. در این صورت گفته میشود که گواهی خودامضا (self-signed) است. اکثر کلاینتهای وب هنگام دریافت یک گواهی خودامضا، یک هشدار نمایش میدهند. این کاری است که Postman در [4] انجام میدهد. سپس اکثر کلاینتهای وب گزینه غیرفعال کردن تأیید اعتبار گواهی امنیتی ارسالشده توسط سرور را ارائه میدهند. این کاری است که Postman در [5] ارائه میدهد؛
ما روی لینک [5] کلیک میکنیم تا تأیید SSL (لایه سوکتهای امن) را غیرفعال کنیم. SSL / TSL (امنیت لایه انتقال) یک پروتکل امنیتی است که یک کانال ارتباطی امن بین دو دستگاه در اینترنت ایجاد میکند. این پروتکلی است که در اینجا توسط آپاچی استفاده میشود. پاسخ به شرح زیر است:

ما همان صفحهای را دریافت میکنیم که با یک مرورگر سنتی دریافت میکردیم. اکنون بیایید به دیالوگ کلاینت/سرور در کنسول Postman (Ctrl-Alt-C) نگاهی بیندازیم:
- خط ۶: هدر HTTP [Host] نام سرور هدفگیریشده توسط کلاینت وب را مشخص میکند. این اصل پشت سرورها مجازی است. در همان آدرس IP (در اینجا 127.0.0.1)، یک وبسرور میتواند چندین وبسایت با نامهای مختلف را میزبانی کند. سربرگ HTTP [Host] به کلاینت اجازه میدهد تا مشخص کند که به کدام سرور (در این مورد، با آدرس 127.0.0.1) در حال آدرسدهی است؛
پس آپاچی چه کاری انجام میدهد؟
وقتی راهاندازی میشود، آپاچی تمام فایلهای پیکربندی یافتشده در [[<laragon>\etc\apache2\sites-enabled]:

هر فایل پیکربندی یک سرور مجازی را تعریف میکند. برای مثال، در فایل [auto.projet-test.test.conf]، خط زیر را مییابیم:
define ROOT "C:/MyPrograms/laragon/www/projet-test/"
define SITE "projet-test.test"
…
خط ۲ سرور مجازی [projet-test.test] را تعریف میکند. فایل [auto.projet-test.test.conf] حاوی پیکربندی این سرور مجازی است. از آنجا که سرور آپاچی هنگام راهاندازی تمام فایلهای پیکربندی را در پوشه [<laragon>\etc\apache2\sites-enabled] میخواند، میداند که یک سرور مجازی به نام [projet-test.test] وجود دارد. بنابراین وقتی درخواست HTTPS را از کلاینت Postman دریافت میکند:
تشخیص میدهد که درخواست به سرور مجازی [projet-test.test] (خط ۶) ارسال شده و این سرور مجازی وجود دارد. سپس از پیکربندی سرور مجازی [projet-test.test] برای پاسخ به کلاینت Postman استفاده میکند.
37.4. ایجاد اولین سرور مجازی آپاچی شما
اکنون که میدانیم سرورهای مجازی برای چه کاری استفاده میشوند و چگونه آنها را پیکربندی کنیم، قصد داریم یکی از آنها را ایجاد کنیم. این سرور برای اجرای یک برنامه پایتون فلکس (Flask) که در دایرکتوری [Apache] نسخه ۱۷ نصب شده است و در حال حاضر روی سرور آپاچی در حال استقرار است، استفاده خواهد شد:
ما برنامهای را که در بخش |link| – یک سرویس وب تاریخ و زمان – توسعه دادهایم، در پوشه [http-servers/12/apache/exemple] قرار دادهایم:

سرور [date_time_server.py] به شرح زیر است:
# وارداتها
import os
import sys
import time
# ما باید پوشهٔ modules را به مسیر پایتون اضافه کنیم
#برای انتقال به آپاچی در ویندوز
sys.path.insert(0, "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages")
from flask import Flask, make_response, render_template
# برنامه Flask
script_dir = os.path.dirname(os.path.abspath(__file__))
application = Flask(__name__, template_folder=f"{script_dir}")
# خانه URL
@application.route('/')
def index():
#ارسال زمان به کلاینت
# time.localtime: تعداد میلیثانیهها از 01/01/1970
# QZXW2HTMLCdGltZS.zdHJmdGltZQZQX امکان قالببندی زمان و تاریخ را فراهم میکند
# قالب نمایش تاریخ و زمان
# d: روز به صورت عدد دو رقمی
# m: ماه دو رقمی
# y: سال (۲ رقم)
# H: ساعت 0.23
# M: دقیقه
# S: ثانیهها
# تاریخ و زمان فعلی
time_of_day = time.strftime('%d/%m/%y %H:%M:%S', time.localtime())
#سند قابل ارسال به مشتری تولید میشود
page = {"date_heure": time_of_day}
document = render_template("date_time_server.html", page=page)
print("document", type(document), document)
# پاسخ HTTP به مشتری
response = make_response(document)
print("response", type(response), response)
return response
# فقط دستی
if __name__ == '__main__':
application.config.update(ENV="development", DEBUG=True)
application.run()
برنامه Flask با شناسه [application] (خطوط 14، 43، 44) ارجاع داده شده است. این نام الزامی است. اگر به برنامه Flask با شناسه متفاوتی ارجاع داده شود، برنامه کار نخواهد کرد و یک پیام خطا نمایش میدهد که بیان میکند نمیتوان URL درخواستی را پیدا کرد. این پیام خطا هیچ نشانهای از منبع خطا ارائه نمیدهد. بنابراین باید در این زمینه مراقب باشید.
فایل HTML که در خط ۳۴ به آن ارجاع شده است، به شرح زیر است:
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Date et heure du moment</title>
</head>
<body>
<b>Date et heure du moment : {{page.date_heure}}</b>
</body>
</html>
- [date-time-server] سرور مجازی میزبان این برنامه خواهد بود. این سرور توسط فایل [<laragon>\etc\apache2\sites-enabled\date-time-server.conf] پیکربندی میشود (توجه داشته باشید که این نام میتواند به صورت آزادانه انتخاب شود – آپاچی تمام فایلهای موجود در [sites-enabled] را برای شناسایی سایتهای مجازی میزبانیشده میخواند)؛
ابتدا این فایل را با کپی کردن فایل [auto.projet-test.test.conf] ایجاد میکنیم و سپس آن را تغییر میدهیم.

فایل [date-time-server.conf] به شکل زیر خواهد بود:
# پوشهای که حاوی اسکریپت پایتون-فلسک برنامه است
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache/exemple"
#نام وبسایتی که توسط این فایل پیکربندی شده است
# در اینجا با نام «تاریخ-زمان-سرور» نامگذاری خواهد شد
# URL به شکل http(s)://date-time-server/path خواهد بود
define SITE "date-time-server"
# آدرس IP 127.0.0.1 را برای سایت SITE در c:/windows/system32/drivers/etc/hosts وارد کنید
#URL HTTP
<VirtualHost *:80>
#با نام مستعار / URL به شکل http(s)://date-time-server/path/... درخواهد آمد
WSGIScriptAlias / "${ROOT}/date_time_server.py"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# URL با امنیت HTTPS
<VirtualHost *:443>
#با نام مستعار / URL به شکل http(s)://date-time-server/path/... درخواهد آمد
WSGIScriptAlias / "${ROOT}/date_time_server.py"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile C:/MyPrograms/laragon/etc/ssl/laragon.crt
SSLCertificateKeyFile C:/myprograms/laragon/etc/ssl/laragon.key
</VirtualHost>
- خط ۷: ما به سرور مجازی پیکربندیشده توسط این فایل، یک نام میدهیم؛
- خط ۲: مقدار متغیر [ROOT] را که در خطوط ۱۴ و ۲۷ استفاده میشود، تنظیم میکنیم؛
- خطوط ۱۴ و ۲۷: مسیر اسکریپت پایتون که هنگام دریافت درخواست توسط سرور مجازی اجرا میشود، مشخص شده است. در اینجا مشخص شده است که درخواستها برای سرور [date-time-server] توسط اسکریپت پایتون [date_time_server.py] پردازش میشوند. این تفاوت با فایل [auto.projet-test.test.conf] از این واقعیت ناشی میشود که فایل اول یک سرور PHP را پیکربندی کرده است، در حالی که فایل [date-time-server.conf] یک سرور پایتون را پیکربندی میکند؛
- خطوط ۱۴ و ۲۷: صفت [WSGIScriptAlias /] در اینجا نشان میدهد که ریشهٔ سرور [date-time-server]، [/] خواهد بود. بنابراین، فایلهای URL در برنامه به شکل [http(s)://date-time-server/chemin] درخواهند آمد؛
- در خطوط ۱۴ و ۲۷، میتوانیم ریشه متفاوتی را به برنامه اختصاص دهیم، برای مثال [WSGIScriptAlias /show]. ورودیهای URL برنامه سپس به شکل [http(s)://show/date-time-server/chemin] درخواهند آمد؛
همچنین باید یک خط به فایل [<windows>/system32/drivers/etc/hosts] اضافه کنیم:
ما خط ۲۵ را اضافه میکنیم تا آدرسهای IP و [127.0.0.1] را به سرور مجازی [date-time-server] اختصاص دهیم.
بیایید بررسی کنیم که همه چیز درست کار میکند. سرور آپاچی را راهاندازی میکنیم:

سپس با استفاده از یک مرورگر، URL [https://date-time-server] را درخواست میکنیم:

- [1]، URL درخواستی؛
- [3]، پاسخ سرور؛
- در [2]، مرورگر نشان میدهد که اتصال HTTPS امن نیست زیرا تشخیص داده است که گواهی ارسالشده توسط سرور آپاچی خودامضا بوده است؛
اکنون، در فایل [date-time-server.conf]، بیایید در خطوط 14 و 27 یک نام مستعار اضافه کنیم:
WSGIScriptAlias /show-date-time "${ROOT}/date_time_server.py"
این تغییر بلافاصله توسط سرور آپاچی منعکس نمیشود. باید آن را دوباره بارگذاری کنید:

سپس URL [https://date-time-server/show-date-time] را درخواست میکنیم. پاسخ سرور به شرح زیر است:

37.5. مهاجرت اپلیکیشن محاسبه مالیات به آپاچی / ویندوز
پوشه [apache] [2] در ابتدا با کپی کردن پوشه [main] ایجاد میشود. مهم است که آنها در یک سطح باشند تا مسیرها در اسکریپت [syspath.py] – که از [1] به [2] کپی شده است – معتبر باقی بمانند. برای جلوگیری از تداخل با عملکرد برنامه [impots / http-servers/ 12]، پیکربندیای را که باید توسط سرور آپاچی اجرا شود، در [apache] قرار میدهیم؛

- فایل [config] از [2] با فایل [config] از [1] یکسان است؛
- فایل [syspath] از [2] با فایل [syspath] از [1] یکسان است؛
- فایل [main_withmysql] از [2] همان فایل [main] از [1] با تغییرات زیر است:
اسکریپت اصلی [main] پارامتر [mysql / pgres] را دریافت کرد که به آن میگفت کدام SGBD را استفاده کند. اسکریپت [main_withmysql] از SGBD و MySQL استفاده میکند:
#انتظار یک پارامتر MySQL یا PostgreSQL را دارد
import os
import sys
#پیکربندی برنامه با MySQL
import config
config = config.configure({'sgbd': "mysql"})
#وابستگیها
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError
…
در خط ۷، SGBD روی MySQL تنظیم شده است.
- فایل [main_withpgres] مشتق شده از [2]، همان فایل [main] مشتق شده از [1] است با تغییرات زیر: این فایل از SGBD و PostgreSQL استفاده میکند:
# یک پارامتر MySQL یا PostgreSQL انتظار میرود
import os
import sys
# برنامه با MySQL پیکربندی شده است
import config
config = config.configure({'sgbd': "pgres"})
#وابستگیها
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError
…
در خط ۷، SGBD را به PostgreSQL تغییر دهید.
پس از انجام این کار، اسکریپت زیر [main_withmysql.wsgi] را ایجاد کنید (پسوند استفاده شده اهمیتی ندارد):
اسکریپت [main_withmysql.wsgi] هدف اجرا شده توسط سرور آپاچی در حالت WSGI خواهد بود:
- هدف سرور آپاچی میتوانست اسکریپت [main_withmysql.py] باشد، همانطور که قبلاً با اسکریپت [date_time_server.py] انجام شد. با این حال، لازم بود کمی تغییر داده شود:
- برخلاف اجرای یک اسکریپت کنسول، در آپاچی، پوشهای که حاوی هدف [main_withmysql.py] است، بخشی از مسیر پایتون (Python Path) نیست. در نتیجه، خط ۶ از اسکریپت [main_withmysql.py] باعث ایجاد خطا میشود؛
- تغییر دوم مورد نیاز این بود که در [main_withmysql]، برنامه Flask با شناسهی [app] ارجاع داده شود. ما میدانیم که برای Apache / WSGI، باید توسط شناسهی [application] نیز ارجاع داده شود؛
- به جای اصلاح [main_withmysql.py]، هدف Apache را تغییر میدهیم. این اکنون اسکریپت [main_withmysql.wsgi] مذکور در بالا خواهد بود:
- خطوط ۱–۷: ما دایرکتوری اسکریپت را به مسیر پایتون (Python Path) اضافه میکنیم. در نتیجه، خط ۶ از [main_withmysql.py] دیگر خطا ایجاد نمیکند؛
- خطوط ۹–۱۰: وارد کردن [main_withmysql.py] باعث اجرای آن میشود. علاوه بر این، ما به برنامه Flask با شناسه [application]، که در [main_withmysql.py] یافت میشود، ارجاع میدهیم؛ این شناسه توسط آپاچی در حالت WSGI مورد نیاز است؛
همین کار برای اسکریپت [main_withpgres.wsgi] نیز انجام میشود:
# پوشهای که حاوی این فایل است
import os
script_dir = os.path.dirname(os.path.abspath(__file__))
# آن را به syspath اضافه میکنیم تا وارد کردن بعدی ممکن شود
import sys
sys.path.insert(0, script_dir)
# برنامهی Flask با نام [app] را وارد میکند و نام آن را به [application] تغییر میدهد
from main_withpgres import app as application
اکنون اهداف اجرایی برای سرور آپاچی را داریم. اکنون باید دو سرور مجازی ایجاد کنیم، یکی برای هر هدف.
در [<laragon>\etc\apache2\sites-enabled]، فایل [flask-impots-withmysql.conf] را ایجاد میکنیم (نام انتخابی اهمیتی ندارد):

# پوشهای که حاوی اسکریپت .wsgi است
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"
#نام وبسایتی است که توسط این فایل پیکربندی شده است
#در اینجا flask-impots-withmysql نامیده میشود
# URL به شکل http(s)://flask-impots-withmysql/path خواهد بود
define SITE "flask-impots-withmysql"
#آدرس IP 127.0.0.1 را برای سایت SITE در c:/windows/system32/drivers/etc/hosts وارد کنید
# مسیرهای کتابخانههای پایتون مورد استفاده را اینجا وارد کنید – آنها را با ویرگول از هم جدا کنید
# کتابخانههای محیط مجازی پایتون را اینجا وارد کنید
WSGIPythonPath "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
# Python Home – تنها در صورتی لازم است که چندین نسخه از پایتون نصب شده باشد
# WSGIPythonHome "C:/Program Files/Python38"
#URL HTTP
<VirtualHost *:80>
# با نام مستعار /، آدرسهای URL به شکل /{prefixe_url}/action/... خواهند بود.
#با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# URL ایمنشده با HTTPS
<VirtualHost *:443>
#با نام مستعار /، آدرسهای URL به شکل /{prefixe_url}/action/... خواهند بود.
#با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias / "${ROOT}/main_withmysql.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile C:/MyPrograms/laragon/etc/ssl/laragon.crt
SSLCertificateKeyFile C:/myprograms/laragon/etc/ssl/laragon.key
</VirtualHost>
- خط ۲: ریشهٔ برنامه، پوشهٔ [apache] که ایجاد کردهایم؛
- خطوط ۲۳ و ۳۸: هدف [main_witmysql.wsgi] که ایجاد کردهایم:
- خط ۷: سرور مجازی [flask-impots-withmysql] نامیده خواهد شد؛
- خط ۱۳: دستور [WSGIPythonPath] به ما امکان میدهد تا پوشهها را به مسیر پایتون (Python Path) اضافه کنیم. در اینجا، آپاچی متوجه نمیشود که ما از یک محیط مجازی برای توسعه برنامه استفاده کردهایم و تمام ماژولهای مورد استفاده توسط برنامه در این محیط مجازی قرار دارند. بنابراین، در خط ۱۳، ما پوشهای را که حاوی تمام ماژولهای محیط مجازی مورد استفاده است، اضافه میکنیم. یک گزینه این است که این پوشه را به مکان دیگری در سیستم فایل کپی کرده و آن مکان را مشخص کنید. گزینه دیگر این است که این پوشه را مستقیماً در داخل هدف [main_witmysql.wsgi] به مسیر پایتون اضافه کنید (این احتمالاً راهحل بهتری است)؛
- خط ۱۶: میتوانیم پوشه نصب پایتون در سیستم فایل را به آپاچی مشخص کنیم. معمولاً این مسیر در PATH ماشین است و این خط اغلب غیرضروری است (که در اینجا نیز همینطور بود). با این حال، ممکن است چندین نصب پایتون روی ماشین وجود داشته باشد و نصب مورد نیاز در PATH ماشین قرار نداشته باشد. در این صورت، این خط مشکل را حل میکند؛
به همین ترتیب، یک فایل [flask-impots-withpgres.conf] ایجاد کنید:
# دایرکتوری اسکریپت .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"
# نام وبسایتی که توسط این فایل پیکربندی شده است
# در اینجا flask-impots-withmysql نامیده میشود
# URL به شکل http(s)://flask-impots-withmysql/path خواهد بود
define SITE "flask-impots-withpgres"
#آدرس IP 127.0.0.1 را برای سایت SITE در c:/windows/system32/drivers/etc/hosts وارد کنید
#مسیرهای کتابخانههای پایتون مورد استفاده را اینجا وارد کنید – آنها را با ویرگول از هم جدا کنید
# کتابخانههای محیط مجازی پایتون را اینجا وارد کنید
WSGIPythonPath "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/venv/lib/site-packages"
# Python Home – تنها در صورتی لازم است که چندین نسخه از پایتون نصب شده باشد
# WSGIPythonHome "C:/Program Files/Python38"
#URL HTTP
<VirtualHost *:80>
# با نام مستعار /، آدرسهای URL به شکل /{prefixe_url}/action/... خواهند بود.
#با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
# URL ایمنشده با HTTPS
<VirtualHost *:443>
# با نام مستعار /، آدرسهای URL به شکل /{prefixe_url}/action/... خواهند بود.
#با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias / "${ROOT}/main_withpgres.wsgi"
DocumentRoot "${ROOT}"
ServerName ${SITE}
ServerAlias *.${SITE}
<Directory "${ROOT}">
AllowOverride All
Require all granted
</Directory>
SSLEngine on
SSLCertificateFile C:/MyPrograms/laragon/etc/ssl/laragon.crt
SSLCertificateKeyFile C:/myprograms/laragon/etc/ssl/laragon.key
</VirtualHost>
تمام این فایلها را ذخیره میکنیم، سرور آپاچی را راهاندازی میکنیم و فرآیندهای SGBD، MySQL و PostgreSQL را اجرا میکنیم. برنامه با پیشوند URL، [/do] و [with_csrftoken=False] پیکربندی شده است (توکن برای CSRF وجود ندارد) در [configs/parameters.py]. ما URL و [https://flask-impots-withmysql/do] را درخواست میکنیم. پاسخ سرور به شرح زیر است:

اکنون URL و [https://flask-impots-pgres/do] را درخواست میکنیم. پاسخ به شرح زیر است:

هر دو برنامه بهطور عادی کار میکنند.
حالا بیایید پارامتر [WSGIScriptAlias] را به [flask-impots-withmysql.conf] تغییر دهیم:
# زیرپوشه اسکریپت .wsgi
define ROOT "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/12/apache"
…
# URL HTTP
<VirtualHost *:80>
# با نام مستعار /، آدرسهای URL به شکل /{prefixe_url}/action/... خواهند بود.
# با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
…
</VirtualHost>
# URL با HTTPS امن شده است
<VirtualHost *:443>
#با نام مستعار /; URL به شکل /{prefixe_url}/action/... خواهد بود
# با نام مستعار /impots، آدرسهای URL به شکل /impots/{prefixe_url}/action/... خواهند بود.
#جایی که [prefixe_url] در QZXW2HTMLCcGFyYW1ldGVycy.weQZQX تعریف شده است
WSGIScriptAlias /impots "${ROOT}/main_withmysql.wsgi"
…
</VirtualHost>
- در خطوط ۱۱ و ۲۰، نام مستعار WSI اکنون به [/impots] تغییر یافته است؛
ما سرور آپاچی را متوقف و مجدداً راهاندازی میکنیم، سپس URL و [https://flask-impots-withmysql/impots/do] را درخواست میکنیم. پاسخ سرور به شرح زیر است:

یک کرش رخ داده است. URL [1] علت را به ما میگوید. باید [https://flask-impots-withmysql/impots/do/afficher-vue-authentification] میبود. نام مستعار WSGI وجود ندارد. این یک خطا در برنامه ما است. این برنامه میتواند پیشوند URL را مدیریت کند (/do واقعاً موجود است). ممکن است فکر کنید که افزودن پیشوند [/impots/do] به برنامه ما، مشکل قبلی را حل میکند. اما نه. سپس با انواع دیگری از مشکلات مواجه میشویم. نام مستعار WSGI مانند یک پیشوند برای URL رفتار نمیکند.
بیایید سعی کنیم بفهمیم چه اتفاقی افتاده است. ما URL و [https://flask-impots-withmysql/impots/do] را درخواست کردیم. ما انتظار داشتیم صفحهٔ احراز هویت را ببینیم. در [1] بالا، میتوانیم ببینیم که برنامه درخواست نمایش صفحه را داده است، اما نه با URL صحیح. بیایید مسیر درخواست [https://flask-impots-withmysql/impots/do] را بررسی کنیم.
ابتدا، مسیر زیر (configs/routes.py) اجرا شد:
#مسیرهای برنامه Flask
#ریشهٔ برنامه
app.add_url_rule(f'{prefix_url}/', methods=['GET'],
view_func=routes.index)
مسیر در خط ۳ در مثال ما [https://flask-impots-withmysql/impots/do] است. میتوانیم ببینیم که بخش «[https://flask-impots-withmysql/impots]» از مسیر حذف شده و تنها «[/do]» باقی مانده است. در مورد بخش «[https://flask-impots-withmysql]»، این امر طبیعی است؛ نام سرور در مسیر گنجانده نمیشود. با این حال، میبینیم که این بخش شامل نام مستعار WSGI [/impots] نیز نمیشود. این یک نکته مهم است. حتی با وجود نام مستعار WSGI، مسیرهای اولیه ما همچنان معتبر باقی میمانند.
حالا ببینیم تابع [index] در خط ۴ (configs/routes_without_csrftoken) چه کاری انجام میدهد:
#ریشهٔ برنامه
def index() -> tuple:
# ارسال مجدد به /init-session/html
return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)
در خط ۴، ما از تابع [init_session] به URL هدایت میشویم. در [configs/routes.py]، این تابع با مسیر [/do/init-session/html] مرتبط شده است:
# init-session
app.add_url_rule(f'{prefix_url}/init-session/<string:type_response>{csrftoken_param}', methods=['GET'],
view_func=routes.init_session)
خط ۲ در تست ما [csrftoken_param] یک رشتهٔ خالی است. برنامه در اینجا توکن CSRF را پردازش نمیکند.
تابع [init_session] به صورت زیر تعریف شده است (configs/routes_without_csrftoken):
# init-session
def init_session(type_response: str) -> tuple:
# اجرای کنترلر مرتبط با اکشن
return front_controller()
در خط ۴، زنجیره پردازش برای اقدام [init-session] آغاز میشود. این زنجیره در [responses/HtmlResponse] به شرح زیر پایان مییابد:
…
#اکنون باید URL هدایت URL تولید شود، بدون فراموش کردن توکن CSRF در صورت نیاز
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
#پاسخ هدایت
return redirect(f"{config['parameters']['prefix_url']}{ads['to']}{csrf_token}"), status.HTTP_302_FOUND
عمل [init-session] یک عمل ADS (عمل انجام کاری) است که با یک هدایت به یک نما در خط 9 پایان مییابد. مشکل از همینجاست. تابع [redirect] در خط ۹، بهطور خودکار alias WSGI را به شیء هدایت URL اضافه نمیکند. این موضوع در تصویر بالا نشان داده شده است. alias /impots از شیء URL که هدایت میشود، حذف شده است.
نسخهٔ زیر مشکل مربوط به alias WSGI را برطرف میکند.


