33. Uygulama alıştırması: sürüm 13
Sürüm 13, sürüm 12'yi aşağıdaki noktalarda değiştirir:
- kodun bazı kısımları yeniden yapılandırılır (refactoring);
- [flask_session] modülü kullanılarak oturum yönetimi farklı bir şekilde gerçekleştirilmiştir;
- yapılandırma dosyasında şifrelenmiş şifreler kullanılmaktadır;
Sürüm 13, başlangıçta sürüm 12'nin kopyalanmasıyla elde edilir:


- [1]'e kopyalandığında, yapılandırma yeniden düzenlenecektir. Özellikle [flask] klasöründen çıkarılır;
- [2]'te ana komut dosyası yeniden düzenlenecek. Ayrıca bu komut dosyası, [flask] klasöründen çıkarılacak;
- [3]'te, yapılandırma birkaç dosyaya bölünecek;
- [4] sürümünde: [main] ana betiği, kodun diğer dosyalara taşınmasıyla basitleştirilecek;
- [5]'te, artık kullanıcı şifreleri şifreleneceği için kimlik doğrulama denetleyicisi değiştirilecektir;
- [6]'te, ana denetleyici daha önce [main] ana betiğinde bulunan kodu içerecek;
33.1. Uygulama yapılandırmasının yeniden düzenlenmesi

Üç yeni yapılandırma dosyası ortaya çıkıyor:
- [mvc]: uygulamanın MVC mimarisini yapılandırmak için;
- [parameters]: Uygulamanın tüm sabitlerini bir araya getirmek için;
- [syspath]: uygulamanın Python yolunu yapılandırır;
[syspath.py] dosyası şu şekildedir:
def configure(config: dict) -> dict:
import os
# bu dosyanın bulunduğu klasör
script_dir = os.path.dirname(os.path.abspath(__file__))
# kök dizin
root_dir = "C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020"
# bağımlılıklar
absolute_dependencies = [
# proje klasörleri
# 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",
# Sabitler, dilimler
f"{root_dir}/impots/v05/entities",
# Günlük Kaydedici, SendAdminMail
f"{root_dir}/impots/http-servers/02/utilities",
# ana komut dosyası klasörü
script_dir,
# yapılandırmalar [database, layers, parameters, controllers, views]
f"{script_dir}/../configs",
# denetleyiciler
f"{script_dir}/../controllers",
# yanıtlar HTTP
f"{script_dir}/../responses",
# görünüm şablonları
f"{script_dir}/../models_for_views",
]
# syspath ayarlanıyor
from myutils import set_syspath
set_syspath(absolute_dependencies)
# yapılandırmayı geri yükleme
return {
"root_dir": root_dir,
"script_dir": script_dir
}
- [syspath] betiği, uygulamanın Python yolunu yapılandırmak için kullanılır (40-41. satırlar);
- diğer yapılandırma komut dosyaları için iki yararlı bilgi sağlar (45-46. satırlar);
[mvc] komut dosyası, jSON / XML / HTML web uygulamasının MVC mimarisini yapılandırır:
def configure(config: dict) -> dict:
# uygulama yapılandırması MVC
# denetleyiciler
from AfficherCalculImpotController import AfficherCalculImpotController
from AuthentifierUtilisateurController import AuthentifierUtilisateurController
from CalculerImpotController import CalculerImpotController
from CalculerImpotsController import CalculerImpotsController
from FinSessionController import FinSessionController
from GetAdminDataController import GetAdminDataController
from InitSessionController import InitSessionController
from ListerSimulationsController import ListerSimulationsController
from MainController import MainController
from SupprimerSimulationController import SupprimerSimulationController
# yanıtlar HTTP
from HtmlResponse import HtmlResponse
from JsonResponse import JsonResponse
from XmlResponse import XmlResponse
# görünüm şablonları
from ModelForAuthentificationView import ModelForAuthentificationView
from ModelForCalculImpotView import ModelForCalculImpotView
from ModelForErreursView import ModelForErreursView
from ModelForListeSimulationsView import ModelForListeSimulationsView
# izin verilen eylemler ve bunların denetleyicileri
controllers = {
# hesaplama oturumunun başlatılması
"init-session": InitSessionController(),
# kullanıcı kimlik doğrulaması
"authentifier-utilisateur": AuthentifierUtilisateurController(),
# bireysel modda vergi hesaplaması
"calculer-impot": CalculerImpotController(),
# toplu modda vergi hesaplaması
"calculer-impots": CalculerImpotsController(),
# simülasyon listesi
"lister-simulations": ListerSimulationsController(),
# simülasyon silme
"supprimer-simulation": SupprimerSimulationController(),
# hesaplama oturumunun sonlandırılması
"fin-session": FinSessionController(),
# vergi hesaplama görünümünün görüntülenmesi
"afficher-calcul-impot": AfficherCalculImpotController(),
# vergi dairesinden verilerin alınması
"get-admindata": GetAdminDataController(),
# ana denetleyici
"main-controller": MainController()
}
# farklı yanıt türleri (json, xml, html)
responses = {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
}
# HTML görünümleri ve bunların şablonları, denetleyici tarafından döndürülen duruma bağlıdır
views = [
{
# kimlik doğrulama görünümü
"états": [
700, # /init-session - başarılı
201, # /kullanıcı-kimlik-doğrulama - başarısız
],
"view_name": "views/vue-authentification.html",
"model_for_view": ModelForAuthentificationView()
},
{
# vergi hesaplama görünümü
"états": [
200, # /kullanıcı-kimlik-doğrulama başarılı
300, # /vergi-hesapla başarılı
301, # /vergi-hesapla başarısız
800, # /vergi-hesaplamasını-göster başarılı
],
"view_name": "views/vue-calcul-impot.html",
"model_for_view": ModelForCalculImpotView()
},
{
# simülasyon listesini görüntüle
"états": [
500, # /simülasyonları-listele başarılı
600, # /simülasyonu-silme başarılı
],
"view_name": "views/vue-liste-simulations.html",
"model_for_view": ModelForListeSimulationsView()
},
]
# beklenmedik hataların görüntülenmesi
view_erreurs = {
"view_name": "views/vue-erreurs.html",
"model_for_view": ModelForErreursView()
}
# yönlendirmeler
redirections = [
{
"états": [
400, # /oturumu-kapatma başarılı
],
# URL'e yönlendirme
"to": "/init-session/html",
}
]
# MVC yapılandırması geri yükleniyor
return {
# denetleyiciler
"controllers": controllers,
# yanıtlar HTTP
"responses": responses,
# görünümler ve şablonlar
"views": views,
# yönlendirme listesi
"redirections": redirections,
# beklenmedik hatalar görünümü
"view_erreurs": view_erreurs
}
- 1-101. satırlar: bu kod bilinmektedir;
- satır 105-116: Uygulamanın MVC yapılandırması geri yüklenir;
[parameters] komut dosyası, uygulamanın sabitlerini bir araya getirir:
def configure(config: dict) -> dict:
# uygulama ayarları
# script_dir
script_dir = config['syspath']['script_dir']
# uygulama yapılandırması
parameters = {
# uygulamayı kullanmaya yetkili kullanıcılar
"users": [
{
"login": "admin",
"password": "$pbkdf2-sha256$29000$mPM.h3COkTIGYOzde68VIg$7LH5Q7rN/1hW.Xa.6rcmR6h52PntvVqd5.na7EtgQNw"
}
],
# günlük dosyası
"logsFilename": f"{script_dir}/../data/logs/logs.txt",
# sunucu yapılandırması SMTP
"adminMail": {
# SMTP sunucusu
"smtp-server": "localhost",
# SMTP sunucusunun bağlantı noktası
"smtp-port": "25",
# yönetici
"from": "guest@localhost.com",
"to": "guest@localhost.com",
# e-posta konusu
"subject": "plantage du serveur de calcul d'impôts",
# SMTP sunucusu yetkilendirme gerektiriyorsa TLS değeri True, aksi takdirde False
"tls": False
},
# iş parçacığı bekleme süresi (saniye cinsinden)
"sleep_time": 0,
# Redis sunucusu
"redis": {
"host": "127.0.0.1",
"port": 6379
},
}
# uygulama ayarları yüklenir
return parameters
- 13. satır: Artık kullanıcı şifreleri şifrelenecektir;
- satır 34-38: [Redis] sunucusunun yapılandırması; buna daha sonra tekrar döneceğiz;
Bu yeni yapılandırma dosyalarıyla birlikte, [config] komut dosyası şu hale gelir:
def configure(config: dict) -> dict:
# syspath yapılandırması
import syspath
config['syspath'] = syspath.configure(config)
# uygulama ayarları
import parameters
config['parameters'] = parameters.configure(config)
# veritabanı yapılandırması
import database
config["database"] = database.configure(config)
# uygulama katmanlarının örneklenmesi
import layers
config['layers'] = layers.configure(config)
# MVC katmanının yapılandırılması [web]
import mvc
config['mvc'] = mvc.configure(config)
# yapılandırma döndürülür
return config
33.2. Ana komut dosyası [main]'in yeniden düzenlenmesi

Ana komut dosyası [main.py], yalnızca sunucuyu başlatmakla yetinir:
# mysql veya pgres parametresi bekleniyor
import sys
syntaxe = f"{sys.argv[0]} mysql / pgres"
erreur = len(sys.argv) != 2
if not erreur:
sgbd = sys.argv[1].lower()
erreur = sgbd != "mysql" and sgbd != "pgres"
if erreur:
print(f"syntaxe : {syntaxe}")
sys.exit()
# uygulama yapılandırılıyor
import config
config = config.configure({'sgbd': sgbd})
# bağımlılıklar
from SendAdminMail import SendAdminMail
from Logger import Logger
from ImpôtsError import ImpôtsError
import redis
# yöneticiye e-posta gönderiliyor
def send_adminmail(config: dict, message: str):
# uygulama yöneticisine e-posta gönderiliyor
config_mail = config['parameters']['adminMail']
config_mail["logger"] = config['logger']
SendAdminMail.send(config_mail, message)
# günlük dosyasının kontrol edilmesi
logger = None
erreur = False
message_erreur = None
try:
# günlük kaydedici
logger = Logger(config['parameters']['logsFilename'])
except BaseException as exception:
# konsol günlüğü
print(f"L'erreur suivante s'est produite : {exception}")
# hatayı not ediyoruz
erreur = True
message_erreur = f"{exception}"
# logger yapılandırmada kaydedilir
config['logger'] = logger
# hata yönetimi
if erreur:
# yöneticiye e-posta gönderme
send_adminmail(config, message_erreur)
# uygulamanın sonlandırılması
sys.exit(1)
# başlangıç günlüğü
log = "[serveur] démarrage du serveur"
logger.write(f"{log}\n")
# Redis sunucusunun kullanılabilirliği kontrol ediliyor
redis_client = redis.Redis(host=config["parameters"]["redis"]["host"],
port=config["parameters"]["redis"]["port"])
# Redis sunucusuna ping atılıyor
try:
redis_client.ping()
except BaseException as exception:
# Redis kullanılamıyor
log = f"[serveur] Le serveur Redis n'est pas disponible : {exception}"
# konsol
print(log)
# günlük
logger.write(f"{log}\n")
# son
sys.exit(1)
# [redis] istemcisi yapılandırmaya kaydediliyor
config['redis_client'] = redis_client
# vergi idaresinden verilerin alınması
erreur = False
try:
# admindata, uygulama kapsamında salt okunur bir veri olacaktır
config["admindata"] = config["layers"]["dao"].get_admindata()
# başarı günlüğü
logger.write("[serveur] connexion à la base de données réussie\n")
except ImpôtsError as ex:
# hata kaydedildi
erreur = True
# hata günlüğü
log = f"L'erreur suivante s'est produite : {ex}"
# konsol
print(log)
# günlük dosyası
logger.write(f"{log}\n")
# yöneticiye e-posta
send_adminmail(config, log)
# ana iş parçacığı artık günlüğe ihtiyaç duymuyor
logger.close()
# hata oluşursa işlem durdurulur
if erreur:
sys.exit(2)
# web uygulamasının rotalarını içe aktarma
import routes
routes.config=config
routes.execute(__name__)
- 56-73. satırlar: Bir Redis sunucusu ve istemcisi eklenmiştir;
- 102-104. satırlar: Uygulamanın rotaları [routes] betiğine taşınmıştır;
33.2.1. [flask_session] ve [redis] modülleri
[Redis] sunucusu, kullanıcı oturumlarını depolamak için kullanılacaktır. Bu oturumları yönetmek için [flask_session] modülünü kullanacağız. Bu modül, kullanıcı oturumlarını çeşitli yerlerde depolayabilir. Redis bunlardan biridir ve biz de onu kullanacağız.
[flask_session] modülü, bir PyCharm terminaline kurulmalıdır:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install flask-session
Collecting flask-session
…
Redis sunucusuyla iletişim kurmak için bir Redis istemcisine ihtiyacımız var. Bu istemci, yine kuracağımız [redis] modülü tarafından sağlanacaktır:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install redis
Collecting redis
…
33.2.2. Redis sunucusu
Redis sunucusu, kullanıcıların oturumlarını depolamak için kullanılacak. [flask_session] modülü şu şekilde çalışır:
- her kullanıcının bir oturum kimliği vardır ve istemciye gönderilen tek şey budur. İstemci, ilk isteğinin ardından yalnızca bir kez oturum çerezi alır. Bu çerez, kullanıcının oturum kimliğini içerir ve bu kimlik, istemcinin sonraki istekleri boyunca değişmez. Bu nedenle sunucunun yeni bir oturum çerezi göndermesi gerekmez;
- önceden oturum içeriği istemciye gönderilirdi. Artık bu durum geçerli olmayacaktır. Kullanıcının oturum içeriği Redis sunucusunda depolanacaktır;
Laragon, varsayılan olarak etkinleştirilmemiş bir Redis sunucusuyla birlikte gelir. Bu nedenle, öncelikle bunu etkinleştirmeniz gerekir:

- [3]'te, [Redis] sunucusunu etkinleştirin;
- [4]'te, Redis istemcilerinin varsayılan olarak kullandığı [6379] bağlantı noktasını bırakın;
Redis etkinleştirildikten sonra Laragon hizmetleri otomatik olarak yeniden başlatılır:

Redis sunucusuna komut modunda sorgu yapılabilir. Bir Laragon terminali açın: [6]:

- [1]'te, [redis-cli] komutu, Redis sunucusunun komut modunda istemciyi başlatır;
Temmuz 2019 itibarıyla Redis istemcisi, sunucuyla iletişim kurmak için 172 komut kullanabilir [https://redis.io/commands#list]. Bunlardan biri olan [command count] [2], bu sayıyı [3] olarak görüntüler.
[Redis]'e yazma işlemi, Redis komutu [set attribut valeur] [4] ile gerçekleştirilir. Değer daha sonra [get attribut] [5] komutuyla alınabilir.
Redis belleğini boşaltmak gerekebilir. Bu işlem, [flushdb] [6] komutuyla gerçekleştirilir. Ardından, [titre] [7] özniteliğinin değerini sorguladığımızda, özniteliğin bulunamadığını belirten [nil] [8] referansı elde edilir. Bir özniteliğin varlığını kontrol etmek için [exists] [9-10] komutunu da kullanabiliriz.
Redis istemcisinden çıkmak için [quit] [11] komutunu girin.
Redis sunucusunda bulunan anahtarları yönetmek için bir web arayüzü de kullanılabilir. Bunun için Laragon'daki Apache sunucusunun çalışıyor olması gerekir:

Aşağıdaki arayüz görüntülenir:

- [1-4], Redis sunucusunda depolanan oturumlardan biri;
33.2.3. Ana komut dosyasında Redis sunucusunun yönetimi [main]
[main] betiği, Redis sunucusunun varlığını şu şekilde kontrol eder:
import redis
…
# Redis sunucusunun kullanılabilirliği kontrol ediliyor
redis_client = redis.Redis(host=config["parameters"]["redis"]["host"],
port=config["parameters"]["redis"]["port"])
# Redis sunucusuna ping atılıyor
try:
redis_client.ping()
except BaseException as exception:
# Redis kullanılamıyor
log = f"[serveur] Le serveur Redis n'est pas disponible : {exception}"
# konsol
print(log)
# günlük
logger.write(f"{log}\n")
# son
sys.exit(1)
# [redis] istemcisi yapılandırmaya kaydediliyor
config['redis_client'] = redis_client
- 4. satır: [redis.Redis] sınıfının oluşturucusu, bir Redis sunucu istemcisi oluşturur. Bu istemcinin özellikleri (adres, bağlantı noktası) [parameters] betiğinde bulunur;
- 8. satır: [ping] yöntemi, Redis sunucusunun mevcut olup olmadığını kontrol eder;
- 9-17. satırlar: ping başarısız olursa, hata günlüğe kaydedilir ve sunucu durdurulur;
- 20. satır: Redis istemcisinin referansı yapılandırmaya eklenir;
33.2.4. Ana komut dosyası [main]'te rota yönetimi
[main]'teki rota yönetimi aşağıdaki satırlarla sınırlıdır:
# web uygulamasının rotaları içe aktarılıyor
import routes
routes.config=config
routes.execute(__name__)
- 1. satır: Yollar, [routes] modülüne taşınmıştır;
- 3. satır: rotaların çalıştırma yapılandırmasını bilmesi gerekir;
- 4. satır: Flask uygulamasını, yürütülen komut dosyasının adını (__main__) geçirerek başlatıyoruz;
Yolların komut dosyası şöyledir:
# bağımlılıklar
import os
from flask import Flask, redirect, request, session, url_for
from flask_api import status
from flask_session import Session
# Flask uygulaması
app = Flask(__name__, template_folder="../flask/templates", static_folder="../flask/static")
# uygulama yapılandırması
config = {}
# ön denetleyici
def front_controller() -> tuple:
# istek ana denetleyiciye yönlendirilir
main_controller = config['mvc']['controllers']['main-controller']
return main_controller.execute(request, session, config)
@app.route('/', methods=['GET'])
def index() -> tuple:
# /init-session/html adresine yönlendirme
return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)
# init-session
@app.route('/init-session/<string:type_response>', methods=['GET'])
def init_session(type_response: str) -> tuple:
# eylemle ilişkili denetleyici çalıştırılır
return front_controller()
# kullanıcı kimlik doğrulaması
@app.route('/authentifier-utilisateur', methods=['POST'])
def authentifier_utilisateur() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# vergi-hesapla
@app.route('/calculer-impot', methods=['POST'])
def calculer_impot() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# toplu vergi hesaplaması
@app.route('/calculer-impots', methods=['POST'])
def calculer_impots():
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# simülasyonları listeleme
@app.route('/lister-simulations', methods=['GET'])
def lister_simulations() -> tuple:
# eyleme bağlı denetleyici çalıştırılır
return front_controller()
# simülasyonu sil
@app.route('/supprimer-simulation/<int:numero>', methods=['GET'])
def supprimer_simulation(numero: int) -> tuple:
# eyleme ilişkin denetleyici çalıştırılır
return front_controller()
# oturum sonu
@app.route('/fin-session', methods=['GET'])
def fin_session() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# vergi-hesaplamasını-göster
@app.route('/afficher-calcul-impot', methods=['GET'])
def afficher_calcul_impot() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# yönetici verilerini al
@app.route('/get-admindata', methods=['GET'])
def get_admindata() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
def execute(name: str):
# oturumun gizli anahtarı
app.secret_key = os.urandom(12).hex()
# Flask-Session
app.config.update(SESSION_TYPE='redis',
SESSION_REDIS=config['redis_client'])
Session(app)
# Flask uygulamasının bir konsol komut dosyası aracılığıyla başlatıldığı durum
if name == '__main__':
app.config.update(ENV="development", DEBUG=True)
app.run(threaded=True)
- 9. satır: Flask uygulaması başlatılır;
- 12. satır: Komut dosyası yazılırken uygulamanın yapılandırması henüz bilinmemektedir. Bu yapılandırma, yalnızca uygulama çalıştırıldığında bilinir;
- 20-77. satırlar: Uygulamanın önceki sürümde tanımlandığı şekliyle rotaları. Bu kısım değişmez;
- 14-18. satırlar: Tüm yollar sadece [front_controller] işlevini çağırmaktadır. Bu işlevin orijinal kodunu kaldırdık. Artık sadece web uygulamasının ana denetleyicisini çağırmaktadır;
- 79-89. satırlar: [execute], web uygulamasını başlatmak için [main] betiği tarafından çağrılan işlevdir;
- 81. satır: [flask_session] modülü, Flask’ın gizli anahtarını kullanır;
- 82-84. satırlar: [flask_session] modülünün yapılandırılması. Bu, [app] adlı Flask uygulamasının [app.config] yapılandırmasına [SESSION_TYPE, SESSION_REDIS] anahtarlarını eklemekten ibarettir:
- [SESSION_TYPE]: oturum türü. Birkaç farklı tür vardır. [redis] türü, [flask_session]'in kullanıcı oturumlarını depolamak için bir [redis] sunucusu kullandığını belirtir. Bu nedenle, bir Redis istemcisinin referansı olması gereken [SESSION_REDIS] anahtarını tanımlamamız gerekir;
- 85. satır: [Flask-Session] oturumu, Flask uygulamasına atanmıştır;
- 86-89. satırlar: 79. satırdaki [name] parametresi [__main__] dizesi ise, Flask uygulaması başlatılır;
33.3. Ana denetleyicinin yeniden yapılandırılması
Daha önce [main] betiğinin [front_controller] işlevinde bulunan kod, ana denetleyiciye taşınmıştır:

# bağımlılıkların içe aktarılması
import threading
import time
from random import randint
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
from Logger import Logger
from SendAdminMail import SendAdminMail
def send_adminmail(config: dict, message: str):
# uygulama yöneticisine e-posta gönderme
config_mail = config['parameters']['adminMail']
config_mail["logger"] = config['logger']
SendAdminMail.send(config_mail, message)
# uygulamanın ana denetleyicisi
class MainController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# isteği işleme
logger = None
action = None
type_response1 = None
try:
# yolun öğeleri alınır
params = request.path.split('/')
# eylem, yolun ilk öğesidir
action = params[1]
# başlangıçta hata yok
erreur = False
# Bazı eylemler için oturum türü önceden bilinmelidir
type_response1 = session.get('typeResponse')
if type_response1 is None and action != "init-session":
# hata kaydediliyor
résultat = {"action": action, "état": 101,
"réponse": ["pas de session en cours. Commencer par action [init-session]"]}
erreur = True
# günlüğe kaydedilir
logger = Logger(config['parameters']['logsFilename'])
# iş parçacığıyla ilişkili bir yapılandırmada saklanıyor
thread_config = {"logger": logger}
thread_name = threading.current_thread().name
config[thread_name] = {"config": thread_config}
# isteği günlüğe kaydedilir
logger.write(f"[MainController] requête : {request}\n")
# istenmişse iş parçacığı durdurulur
sleep_time = config['parameters']['sleep_time']
if sleep_time != 0:
# Bazı iş parçacıklarının durdurulması, diğerlerinin ise durdurulmaması için duraklama süresi rastgele belirlenir
aléa = randint(0, 1)
if aléa == 1:
# duraklama öncesi günlük kaydı
logger.write(f"[front_controller] mis en pause du thread pendant {sleep_time} seconde(s)\n")
# duraklama
time.sleep(sleep_time)
# bazı işlemler için kimlik doğrulaması gerekiyor
user = session.get('user')
if not erreur and user is None and action not in ["init-session", "authentifier-utilisateur"]:
# hata kaydediliyor
résultat = {"action": action, "état": 101,
"réponse": [f"action [{action}] demandée par utilisateur non authentifié"]}
erreur = True
# Hata var mı?
if erreur:
# istek geçersiz
status_code = status.HTTP_400_BAD_REQUEST
else:
# eylemle ilişkili denetleyici çalıştırılıyor
controller = config['mvc']['controllers'][action]
résultat, status_code = controller.execute(request, session, config)
except BaseException as exception:
# diğer (beklenmeyen) istisnalar
résultat = {"action": action, "état": 131, "réponse": [f"{exception}"]}
status_code = status.HTTP_400_BAD_REQUEST
finally:
pass
# Müşteriye gönderilen sonuç günlüğe kaydediliyor
log = f"[MainController] {résultat}\n"
logger.write(log)
# Önemli bir hata mı oluştu?
if status_code == status.HTTP_500_INTERNAL_SERVER_ERROR:
# uygulama yöneticisine bir e-posta gönderiliyor
send_adminmail(config, log)
# yanıt için istenen tür belirlenir
type_response2 = session.get('typeResponse')
if type_response2 is None and type_response1 is None:
# oturum türü henüz belirlenmemiştir - bu, jSON olacaktır
type_response = 'json'
elif type_response2 is not None:
# yanıt türü biliniyor ve oturumda mevcut
type_response = type_response2
else:
# aksi takdirde type_response1 kullanılmaya devam edilir
type_response = type_response1
# gönderilecek yanıt oluşturulur
response_builder = config['mvc']['responses'][type_response]
response, status_code = response_builder \
.build_http_response(request, session, config, status_code, résultat)
# açılmışsa günlük dosyasını kapatırız
if logger:
logger.close()
# yanıt gönderilir HTTP
return response, status_code
Bu kodun tamamı daha önce bir şekilde görülmüştü.
33.4. Şifrelenmiş şifrelerin yönetimi
Şifrelenmiş şifreleri yönetmek için, Pycharm terminalinden kurduğumuz [passlib] modülünü kullanacağız:
(venv) C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\packages>pip install passlib
Collecting passlib
…
İşte, parametre olarak verilen şifreyi şifreleyen bir komut dosyası örneği:

[create_hashed_password] komut dosyası şu şekildedir (https://passlib.readthedocs.io/en/stable/):
import sys
# şifreleme işlevi
from passlib.hash import pbkdf2_sha256
# şifrelenecek şifre bekleniyor
syntaxe = f"{sys.argv[0]} password"
erreur = len(sys.argv) != 2
if erreur:
print(f"syntaxe : {syntaxe}")
sys.exit()
else:
password = sys.argv[1]
# şifre şifreleniyor
hash = pbkdf2_sha256.hash(password)
print(f"version cryptée de [{password}] = {hash}")
# doğrulama
correct = pbkdf2_sha256.verify(password, hash)
print(correct)
- 16. satır: parametre olarak verilen şifre şifrelenir;
- 20. satır: parametre olarak verilen [password] şifresini, şifrelenmiş hali olan [hash] ile karşılaştırır. [verify] işlevi, [password] şifresini şifreler ve elde edilen şifrelenmiş dizeyi [hash] ile karşılaştırır. İki dize eşitse True değerini döndürür;
Yukarıdaki komut dosyası, [admin] şifresinin şifrelenmiş halini elde etmemizi sağlar:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/impots/http-servers/08/passlib/create_hashed_password.py admin
version cryptée de [admin] = $pbkdf2-sha256$29000$fU9pTendO6c0ZoyR8r5Xqg$5ZXywIUnbMfN2hPnBaefiuqWjEbmAY.Lu06i4dwcnek
True
- satırda, [parameters] betiğine girdiğimiz değer:
"users": [
{
"login": "admin",
"password": "$pbkdf2-sha256$29000$fU9pTendO6c0ZoyR8r5Xqg$5ZXywIUnbMfN2hPnBaefiuqWjEbmAY.Lu06i4dwcnek"
}
],
[AuthentifierUtilisateurController] kimlik doğrulama denetleyicisi şu şekilde değişir:
from passlib.handlers.pbkdf2 import pbkdf2_sha256
…
# (kullanıcı adı, şifre) çiftinin geçerliliği kontrol ediliyor
users = config['parameters']['users']
i = 0
nbusers = len(users)
trouvé = False
while not trouvé and i < nbusers:
trouvé = user == users[i]["login"] and pbkdf2_sha256.verify(password, users[i]["password"])
i += 1
# bulundu mu?
if not trouvé:
…
33.5. Testler
Tarayıcıyla yapılan testlerin yanı sıra, sürüm 12 için yazılmış [http-servers/07] klasöründeki istemciler de kullanılabilir. Bunlar sürüm 13 için de "çalışmalıdır":

- [1]'te üç istemci de çalışmalıdır;
- [2]'te, iki testin de çalışması gerekir;