35. Uygulama alıştırması: sürüm 15
35.1. Giriş
Bu sürüm, tarayıcıda uygulama sayfalarının yenilenmesiyle ilgili sorunları çözmeyi amaçlamaktadır (F5). Bir örnek verelim. Kullanıcı az önce id=3 simülasyonunu sildi:

Silme işleminden sonra tarayıcıdaki URL, [/supprimer-simulation/3/…] olur. Kullanıcı sayfayı yenilerse (F5), URL ve [1] yeniden oynatılır. Dolayısıyla, id=3 simülasyonunun silinmesi talebi yeniden yapılır. Sonuç şu şekilde olur:

İşte başka bir örnek. Kullanıcı az önce bir vergi hesapladı:
- [1]'te, az önce POST ile sorgulanan URL ve [/calculer-impot];
Kullanıcı sayfayı yenilerse (F5), bir uyarı mesajı alır:

Sayfa yenileme işlemi, tarayıcı tarafından en son yürütülen isteği, bu örnekte [POST /calculer-impot]’i, yeniden çalıştırır. POST'in yeniden yürütülmesi istendiğinde, tarayıcılar yukarıdakine benzer bir uyarı verir. Bu uyarı, daha önce gerçekleştirilmiş bir eylemin yeniden yürütüldüğünü bildirir. Diyelim ki bu POST bir satın alma işlemi gerçekleştirmiş olsun; bu işlemi tekrarlamak talihsiz bir durum olur.
Ayrıca, kullanıcının tarayıcısına URL gibi kodları girme olasılığını da sınırlayacağız. Örneğin, önceki ekran görüntülerinden birine bakalım:

Bu sayfada sunulan bağlantılar şunlardır:
- [Liste des simulations], URL ve [/lister-simulations] ile ilişkilidir;
- [Fin de session], URL ve [/fin-session] ile ilişkilidir;
- [Valider], URL (yukarıda görünmüyor) ve [/calculer-impot] ile ilişkilidir;
Vergi hesaplama ekranı görüntülendiğinde, yalnızca [/lister-simulations, /fin-session, /calculer-impot] eylemlerini kabul edeceğiz. Kullanıcı tarayıcısına başka bir eylem yazarsa, bir hata bildirilir. Uygulamanın dört ekranı için de bu tür bir doğrulama yapılacaktır.
Sayfa yenileme sorununu şu şekilde çözmeyi öneriyoruz:
- iki tür eylem ayırt edeceğiz:
- uygulamanın durumunu değiştiren ADS eylemleri (Action Do Something). ADS eylemleri genellikle URL'te veya isteğin gövdesinde parametrelere sahiptir;
- uygulamanın durumunu değiştirmeden bir görünüm görüntüleyen ASV eylemleri (Action Show View). V görünümlerinin sayısı kadar ASV eylemi olacaktır. ASV eylemlerinin hiçbir parametresi yoktur;
- ADS eylemleri şimdiye kadar, ilgili görünümün M modelini hazırladıktan sonra bir V görünümünü görüntüleyerek yürütülüp sonlanıyordu. Artık, bu eylemler görünümün M şablonunu oturuma yükleyecek ve tarayıcıdan, V görünümünü görüntülemekle görevli ASV eylemine yönlendirilmesini isteyecek;
- V görünümleri ancak ASV eylemi tamamlandıktan sonra görüntülenecektir. Bu eylemler, şablonlarını oturumdan alacaktır;
Bu yöntemin avantajı, tarayıcının adres çubuğunda URL ASV adresini gösterecek olmasıdır. Sayfa yenilendiğinde ASV eylemi yeniden çalıştırılacaktır. Bu eylem, uygulamanın durumunu değiştirmez ve oturum şablonunu kullanır. Dolayısıyla aynı sayfa, herhangi bir yan etki olmaksızın yeniden görüntülenecektir. Sonuç olarak, yönlendirmeler nedeniyle kullanıcı tarayıcısında yalnızca URL eylemlerini görecek ve sayfadan sayfaya geziniyormuş gibi hissedecektir;
35.2. Uygulama

[impots/http-servers/10] dosyası, başlangıçta [impots/http-servers/09] dosyasının kopyalanmasıyla elde edilir. Daha sonra bu dosya değiştirilir.
35.2.1. Yeni yollar

[routes_with_csrftoken] ve [routes_without_csrftoken] dosyalarında, dört görünümü görüntüleyen dört ASV eyleminin dört rotasını oluşturmamız gerekir. Diğer rotalar aynı kalır.
[routes_with_csrftoken] dosyasında:
# vergi hesaplama görünümünü göster
@app.route('/afficher-vue-calcul-impot/<string:csrf_token>', methods=['GET'])
def afficher_vue_calcul_impot(csrf_token: str) -> tuple:
# eylemle ilişkili denetleyici çalıştırılıyor
return front_controller()
# kimlik doğrulama görünümünü göster
@app.route('/afficher-vue-authentification/<string:csrf_token>', methods=['GET'])
def afficher_vue_authentification(csrf_token: str) -> tuple:
# eyleme bağlı denetleyici çalıştırılıyor
return front_controller()
# simülasyon-listesi-görünümü-göster
@app.route('/afficher-vue-liste-simulations/<string:csrf_token>', methods=['GET'])
def afficher_vue_liste_simulations(csrf_token: str) -> tuple:
# eyleme bağlı denetleyici çalıştırılır
return front_controller()
# hata-listesi-görünümü-göster
@app.route('/afficher-vue-liste-erreurs/<string:csrf_token>', methods=['GET'])
def afficher_vue_liste_erreurs(csrf_token: str) -> tuple:
# eyleme bağlı denetleyici çalıştırılıyor
return front_controller()
1-23. satırlarda, dört eylem için dört rota oluşturduk ASV:
- [/afficher-vue-authentification], 8. satır, kimlik doğrulama görünümünü görüntüler;
- [/afficher-vue-calcul-impot], 2. satır, vergi hesaplama görünümünü görüntüler;
- [/afficher-vue-liste-simulations], satır 14, simülasyonlar görünümünü görüntüler;
- [/afficher-vue-liste-erreurs], 20. satır, beklenmedik hatalar görünümünü gösterir;
Aynı işlem, jetonsuz yolların bulunduğu [routes_with_csrftoken] dosyasında da yapılır:
# yollar ASV -------------------------
# vergi-hesaplama-görünümü-göster
@app.route('/afficher-vue-calcul-impot', methods=['GET'])
def afficher_vue_calcul_impot() -> tuple:
# eyleme ilişkin denetleyici çalıştırılır
return front_controller()
# görünüm-görüntüle-kimlik-doğrulama
@app.route('/afficher-vue-authentification', methods=['GET'])
def afficher_vue_authentification() -> tuple:
# eyleme ilişkin denetleyici çalıştırılıyor
return front_controller()
# simülasyon-listesini-göster
@app.route('/afficher-vue-liste-simulations', methods=['GET'])
def afficher_vue_liste_simulations() -> tuple:
# eyleme bağlı denetleyici çalıştırılır
return front_controller()
# hata-listesi-görünümü-göster
@app.route('/afficher-vue-liste-erreurs', methods=['GET'])
def afficher_vue_liste_erreurs() -> tuple:
# eyleme bağlı denetleyici çalıştırılır
return front_controller()
35.2.2. Yeni kontrolörler

[AfficherVueAuthentificationController] denetleyicisi, ASV [/afficher-vue-authentification] eylemini yürütür:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueAuthentificationController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# yolun öğeleri alınır
params = request.path.split('/')
action = params[1]
# görünüm değişimi - sadece bir durum kodu ayarlanması gerekiyor
return {"action": action, "état": 1100, "réponse": ""}, status.HTTP_200_OK
ASV eylemlerinin hiçbir parametresi olmadığı ve uygulamanın durumunu değiştirmediği belirtilmişti. 14. satırda bir durum kodu ayarlayarak istenen görünümü görüntülemekle yetiniyoruz.
[AfficherVueCalculImpotController] denetleyicisi, ASV eylemini yürütür. [/afficher-vue-calcul-impot]:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueCalculImpotController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# yolun öğeleri alınır
params = request.path.split('/')
action = params[1]
# görünüm değişikliği - sadece bir durum kodu ayarlanması gerekiyor
return {"action": action, "état": 1400, "réponse": ""}, status.HTTP_200_OK
[AfficherVueListeSimulationsController] denetleyicisi, ASV ve [/afficher-vue-liste-simulations] eylemlerini yürütür:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueListeSimulationsController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# yolun öğelerini alıyoruz
params = request.path.split('/')
action = params[1]
# görünüm değişikliği - sadece bir durum kodu ayarlanması gerekiyor
return {"action": action, "état": 1200, "réponse": ""}, status.HTTP_200_OK
[AfficherVueListeErreursController] denetleyicisi, ASV eylemini yürütür [/afficher-vue-liste-erreurs]:
from flask_api import status
from werkzeug.local import LocalProxy
from InterfaceController import InterfaceController
class AfficherVueListeErreursController(InterfaceController):
def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
# yolun öğelerini alıyoruz
params = request.path.split('/')
action = params[1]
# görünüm değişikliği - sadece bir durum kodu ayarlanması gerekiyor
return {"action": action, "état": 1300, "réponse": ""}, status.HTTP_200_OK
Durum kodlarını özetleyelim:
- 1100 kodu, kimlik doğrulama ekranını göstermelidir;
- 1400 kodu, vergi hesaplama ekranını göstermelidir;
- 1200 kodu simülasyon görünümünü göstermelidir;
- 1300 kodu, beklenmedik hataların görünümünü göstermelidir;
35.2.3. Yeni yapılandırma MVC

Boyutu çok büyük hale geldiği için, MVC yapılandırması dört dosyaya bölünmüştür:
- [controllers]: MVC uygulamasının C denetleyicileri listesi;
- [ads_actions]: ADS eylemlerinin listesi (Do Something Eylemi);
- [asv_actions]: ASV (Action Show View) eylemlerinin listesi;
- [responses]: Uygulamanın HTTP yanıt sınıflarını listeler;
En basit olanından başlayalım: HTTP ve [responses] yanıt dosyaları:
def configure(config: dict) -> dict:
# uygulama yapılandırması MVC
# yanıtlar HTTP
from HtmlResponse import HtmlResponse
from JsonResponse import JsonResponse
from XmlResponse import XmlResponse
# farklı yanıt türleri (json, xml, html)
responses = {
"json": JsonResponse(),
"html": HtmlResponse(),
"xml": XmlResponse()
}
# HTTP yanıt sözlüğü oluşturuluyor
return {
# HTTP yanıtları
"responses": responses,
}
Sürpriz yok.
Denetleyicilerin [controllers] dosyası da sürpriz içermiyor. Buraya sadece ASV eylemlerinin yeni denetleyicileri eklenmiş.
def configure(config: dict) -> dict:
# MVC uygulamasının yapılandırması
# ana denetleyici
from MainController import MainController
# eylem denetleyicileri ADS
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 SupprimerSimulationController import SupprimerSimulationController
from AfficherCalculImpotController import AfficherCalculImpotController
# eylem denetleyicileri ASV
from AfficherVueCalculImpotController import AfficherVueCalculImpotController
from AfficherVueAuthentificationController import AfficherVueAuthentificationController
from AfficherVueListeErreursController import AfficherVueListeErreursController
from AfficherVueListeSimulationsController import AfficherVueListeSimulationsController
# yetkili eylemler ve bunların denetleyicileri
controllers = {
# hesaplama oturumunun başlatılması
"init-session": InitSessionController(),
# kullanıcı kimlik doğrulaması
"authentifier-utilisateur": AuthentifierUtilisateurController(),
# vergi hesaplama görünümüne bağlantı
"afficher-calcul-impot": AfficherCalculImpotController(),
# 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 dairesinden veri alınması
"get-admindata": GetAdminDataController(),
# ana denetleyici
"main-controller": MainController(),
# kimlik doğrulama ekranının görüntülenmesi
"afficher-vue-authentification": AfficherVueAuthentificationController(),
# vergi hesaplama ekranının görüntülenmesi
"afficher-vue-calcul-impot": AfficherVueCalculImpotController(),
# simülasyonlar ekranı
"afficher-vue-liste-simulations": AfficherVueListeSimulationsController(),
# hata görünümünün görüntülenmesi
"afficher-vue-liste-erreurs": AfficherVueListeErreursController()
}
# denetleyicilerin yapılandırması
return {
# denetleyiciler
"controllers": controllers,
}
ASV eylemlerinin [asv_actions] yapılandırması şu şekildedir:
def configure(config: dict) -> dict:
# uygulama yapılandırması MVC
# HTML görünümleri ve şablonları, denetleyici tarafından döndürülen duruma bağlıdır
# ASV eylemleri (Görünümü Göster eylemi)
asv = [
{
# kimlik doğrulama görünümü
"états": [
1100, # /kimlik-doğrulama-görünümünü-göster
],
"view_name": "views/vue-authentification.html",
},
{
# vergi hesaplama görünümü
"états": [
1400, # /vergi-hesaplama-görünümünü-göster
],
"view_name": "views/vue-calcul-impot.html",
},
{
# simülasyon listesi görünümü
"états": [
1200, # /simülasyon-listesi-görünümü-göster
],
"view_name": "views/vue-liste-simulations.html",
},
{
# hata listesi görünümü
"états": [
1300, # /hata-listesi-görünümü-göster
],
"view_name": "views/vue-erreurs.html",
},
]
# ASV yapılandırması oluşturuluyor
return {
# görünümler ve şablonlar
"asv": asv,
}
- [asv_actions] dosyası, işleyişi aşağıda özetlenen dört yeni eylemi bir araya getirir:
- hiçbir parametreleri yoktur;
- oturumda bulunan bir şablona sahip belirli bir görünümü görüntüler;
- 6-35. satırlardaki [asv] listesi, her bir ASV eylemine bir görünüm atar;
[ads_actions] dosyası, ADS eylemlerini bir araya getirir:
def configure(config: dict) -> dict:
# MVC uygulama yapılandırması
# görünüm şablonları
from ModelForAuthentificationView import ModelForAuthentificationView
from ModelForCalculImpotView import ModelForCalculImpotView
from ModelForErreursView import ModelForErreursView
from ModelForListeSimulationsView import ModelForListeSimulationsView
# ADS eylemleri (Do Something Eylemi)
ads = [
{
"états": [
400, # /oturum sonlandırma başarılı
],
# ADS eylemine yönlendirme
"to": "/init-session/html",
},
{
"états": [
700, # /oturum-başlatma - başarılı
201, # /kullanıcı-doğrulama başarısız
],
# ASV eylemine yönlendirme
"to": "/afficher-vue-authentification",
# sonraki görünüm şablonu
"model_for_view": ModelForAuthentificationView()
},
{
"é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ğlantı
],
# ASV eylemine yönlendirme
"to": "/afficher-vue-calcul-impot",
# sonraki görünüm şablonu
"model_for_view": ModelForCalculImpotView()
},
{
"états": [
500, # /simülasyonları-listele başarılı
600, # /simülasyonu-silme başarılı
],
# ASV eylemine yönlendirme
"to": "/afficher-vue-liste-simulations",
# sonraki görünüm şablonu
"model_for_view": ModelForListeSimulationsView()
},
]
# beklenmedik hatalar görünümü
view_erreurs = {
# ASV eylemine yönlendirme
"to": "/afficher-vue-liste-erreurs",
# sonraki görünüm şablonu
"model_for_view": ModelForErreursView()
}
# MVC yapılandırması oluşturuluyor
return {
# ADS eylemleri
"ads": ads,
# beklenmedik hataların görünümü
"view_erreurs": view_erreurs,
}
- 11-51. satırlar: ADS eylemlerinin listesi (Do Something Eylemi). Önceki sürümlerdeki tüm eylemler burada da bulunur. Ancak işleyişleri değişmiştir:
- V görünümünü görüntülemezler. Yalnızca bu V görünümünün M modelini hazırlarlar;
- V görünümünün görüntülenmesini, V görünümüyle ilişkili ASV eylemine yönlendirme yoluyla talep ederler;
- ADS eylemlerinin hepsi ASV eylemine yönlendirmeye yol açmaz: 12-18. satırlarda, ADS ve [/fin-session] eylemleri, ADS ve [/init-session/html] eylemlerine yönlendirmeye yol açar. ADS -> ADS ve ADS -> ASV yönlendirmelerini birbirinden ayırt etmek için [model_for_view] şablonundan yararlanılabilir. Ancak bu şablon, ADS -> ADS yönlendirmeleri için mevcut değildir;
Tüm yapılandırmaları bir araya getiren [main/config] dosyası şu şekilde değişir:
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]
config['mvc'] = {}
# [web] katmanındaki denetleyicilerin yapılandırılması
import controllers
config['mvc'].update(controllers.configure(config))
# ASV eylemleri (Görünümü Göster Eylemi)
import asv_actions
config['mvc'].update(asv_actions.configure(config))
# ADS eylemleri (Do Something Eylemi)
import ads_actions
config['mvc'].update(ads_actions.configure(config))
# yanıt yapılandırması HTTP
import responses
config['mvc'].update(responses.configure(config))
# yapılandırmayı geri yükleme
return config
35.2.4. Yeni şablonlar

Şablonlar yeni bilgiler oluşturacaktır. Örneğin, kimlik doğrulama ekranının şablonunu ele alalım:
from flask import Request
from werkzeug.local import LocalProxy
from AbstractBaseModelForView import AbstractBaseModelForView
class ModelForAuthentificationView(AbstractBaseModelForView):
def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
# sayfa verilerini şablona yerleştiriyoruz
modèle = {}
…
# CSRF jetonu
modèle['csrf_token'] = super().get_csrftoken(config)
# görünümden gerçekleştirilebilecek eylemler
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
# şablonu işlenir
return modèle
- Yenilik 17. satırda yer alıyor: Her şablon, şablon M olduğu V görünümü görüntülenirken gerçekleştirilebilecek eylemlerin bir listesini oluşturacak. Bu eylemleri öğrenmek için V görünümüne geri dönmek gerekiyor. Kimlik doğrulama görünümü örneğinde:

- görüldüğü gibi, kimlik doğrulama görünümü yalnızca bir eylem sunmaktadır; bu da [Valider] düğmesine ait eylemdir. Bu eylem [/authentifier-utilisateur]'tir;
Şöyle yazmıştık:
# görünümden gerçekleştirilebilecek eylemler
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
Yukarıdaki görünümde, kullanıcının sayfayı yenilemesi durumunda [1] [/afficher-vue-authentification] eyleminin yeniden çalıştırılacağı görülmektedir. Bu nedenle eylemin yetkilendirilmesi gerekir. Yetkilendirilmeseydi, kullanıcı sayfayı her yenilediğinde bir hata ile karşılaşırdı. Bunun istenmeyen bir durum olduğu düşünülmüştür.
Olası eylemler, görünüm şablonuna eklenir. Bu şablonun oturumda saklanacağı bilinmektedir.
Dört görünümün her biri için de aynı işlem yapılır. O halde, gerçekleştirilebilecek eylemler şunlardır:
Kimlik doğrulama görünümü
modèle['actions_possibles'] = ["afficher-vue-authentification", "authentifier-utilisateur"]
Vergi hesaplama görünümü
# görünümden gerçekleştirilebilecek işlemler
modèle['actions_possibles'] = ["afficher-vue-calcul-impot", "calculer-impot", "lister-simulations","fin-session"]
Simülasyon listesi görünümü
# görünümden gerçekleştirilebilecek eylemler
actions_possibles = ["afficher-vue-liste-simulations", "afficher-calcul-impot", "fin-session"]
if len(modèle['simulations']) != 0:
actions_possibles.append("supprimer-simulation")
modèle['actions_possibles'] = actions_possibles
Hata listesi görünümü
# görünümden gerçekleştirilebilecek işlemler
modèle['actions_possibles'] = ["afficher-vue-liste-erreurs", "afficher-calcul-impot", "lister-simulations", "fin-session"]
35.2.5. Yeni ana denetleyici

[MainController] denetleyicisinde bazı değişiklikler yapıldı:
# bağımlılıkların içe aktarılması
import threading
import time
from random import randint
from flask_api import status
from flask_wtf.csrf import generate_csrf, validate_csrf
from werkzeug.local import LocalProxy
from wtforms import ValidationError
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önderilir
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):
# istek işleniyor
type_response1 = None
logger = None
try:
# yolun öğeleri alınmaktadır
params = request.path.split('/')
# eylem, yolun ilk öğesidir
action = params[1]
# günlüğe kaydediliyor
logger = Logger(config['parameters']['logsFilename'])
…
# /afficher-vue-liste-erreurs eylemi özel bir eylemdir
# hiçbir doğrulama yapılmaz, aksi takdirde sonsuz bir yönlendirme döngüsüne girme riski vardır
erreur = False
if action != "afficher-vue-liste-erreurs":
# hata varsa, [sonuç] müşteriye gönderilecek sonuçtur
(erreur, résultat, type_response1) = MainController.check_action(params, session, config)
# Hata yoksa - eylem yürütülür
if not erreur:
# eyleme ilişkin denetleyici çalıştırılır
controller = config['mvc']['controllers'][action]
résultat, status_code = controller.execute(request, session, config)
except BaseException as exception:
# (beklenmeyen) istisnalar
résultat = {"action": action, "état": 131, "réponse": [f"{exception}"]}
erreur = True
finally:
pass
# çalıştırma hatası
if erreur:
# hatalı istek
status_code = status.HTTP_400_BAD_REQUEST
if config['parameters']['with_csrftoken']:
# sonuca csrf_token eklenir
résultat['csrf_token'] = generate_csrf()
….
# HTTP yanıtı gönderiliyor
return response, status_code
@staticmethod
def check_action(params: list, session: LocalProxy, config: dict) -> (bool, dict, str):
…
# yöntemin sonucu
return erreur, résultat, type_response1
- 39-44. satırlar: Eylem üzerinde ilk doğrulamalar yapılır. Buna daha sonra tekrar döneceğiz. [MainController.check_action] statik yöntemi, üç elemanlı bir tuple döndürür:
- [erreur]: Bir hata tespit edilirse True, aksi takdirde False;
- [résultat]: (hata==True ise) bir hata sonucu, aksi takdirde None;
- [type_response1]: oturumun türü (json, xml, html, None);
- 39. satır: Eylem, hata listesini görüntüleyecek olan ASV veya [afficher-vue-liste-erreurs] ise herhangi bir kontrol yapılmaz. Aslında, bu eylem sırasında bir hata bulunursa, tekrar [/afficher-vue-liste-erreurs] eylemine yönlendirilirdik ve sonsuz bir yönlendirme döngüsüne girerdik;
- [check_action] statik yöntemi şu şekildedir:
@staticmethod
def check_action(params: list, session: LocalProxy, config: dict) -> (bool, dict, str):
# devam eden eylemi alıyoruz
action = params[1]
# başlangıçta hata ve sonuç yok
erreur = False
résultat = None
# bazı işlemlerden önce oturum türü bilinmelidir ADS
type_response1 = session.get('typeResponse')
if type_response1 is None and action != "init-session":
# hata kaydedildi
résultat = {"action": action, "état": 101,
"réponse": ["pas de session en cours. Commencer par action [init-session]"]}
erreur = True
# Bazı işlemler için ADS kimlik doğrulaması yapılmalıdır
user = session.get('user')
if user is None and action not in ["init-session",
"authentifier-utilisateur",
"afficher-vue-authentification"]:
# hata notu
résultat = {"action": action, "état": 101,
"réponse": [f"action [{action}] demandée par utilisateur non authentifié"]}
erreur = True
# bir görünümden yalnızca belirli işlemler yapılabilir
if not erreur and action != "init-session":
# Şu anda gerçekleştirilebilecek işlem yok
actions_possibles = None
# varsa, oturumdaki gelecekteki görünümün şablonu alınır
modèle = session.get('modèle')
# eğer bir şablon bulunursa, bu şablonun mümkün olan eylemleri alınır
if modèle:
actions_possibles = modèle.get('actions_possibles')
# Olası eylemlerin bir listesi varsa, mevcut eylemin bu listeye dahil olup olmadığı kontrol edilir
if actions_possibles and action not in actions_possibles:
# hatayı not ederiz
résultat = {"action": action, "état": 151,
"réponse": [f"action [{action}] incorrecte dans l'environnement actuel"]}
erreur = True
# hata mı?
if not erreur and config['parameters']['with_csrftoken']:
# CSRF jetonunun geçerliliğini kontrol ederiz
# csrf_token, yolun son elemanıdır
csrf_token = params.pop()
try:
# csrf_token geçersizse bir istisna atılacaktır
validate_csrf(csrf_token)
except ValidationError as exception:
# geçersiz CSRF jetonu
résultat = {"action": action, "état": 121, "réponse": [f"{exception}"]}
# hata kaydedildi
erreur = True
# yöntemin sonucu
return erreur, résultat, type_response1
- 2. satır: [check_action] yöntemi, devam eden işlemin geçerliliği konusunda çeşitli kontroller yapar;
- 6-26, 45-57. satırlar: önceki sürümlerde de bu kontroller zaten yapılıyordu;
- satır 28-42: yeni bir kontrol eklenmiştir. Uygulamanın mevcut durumunda devam eden işlemin mümkün olup olmadığı kontrol edilir. Devam eden işlem mümkün değilse, mevcut işlemin beklenmedik hatalar sayfasına yönlendirileceğini garanti eden 151 durumu oluşturulur (satır 40);
35.2.6. Yeni yanıt HTML

Yapılmakta olan değişiklikler yalnızca HTML oturumlarını ilgilendirir. jSON veya XML oturumları etkilenmez. [HtmlResponse] sınıfı şu şekilde değiştirilir:
# bağımlılıklar
from flask import make_response, redirect, render_template
from flask.wrappers import Response
from flask_api import status
from flask_wtf.csrf import generate_csrf
from werkzeug.local import LocalProxy
from InterfaceResponse import InterfaceResponse
class HtmlResponse(InterfaceResponse):
def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
résultat: dict) -> (Response, int):
# HTML yanıtı, denetleyici tarafından döndürülen durum koduna bağlıdır
état = résultat["état"]
# durumun bir eylem tarafından oluşturulup oluşturulmadığı araştırılır ASV
# bu durumda bir görünüm görüntülenmelidir
asv_configs = config['mvc']["asv"]
trouvé = False
i = 0
# görünüm listesi taranır
nb_views = len(asv_configs)
while not trouvé and i < nb_views:
# i numaralı görünüm
asv_config = asv_configs[i]
# i numaralı görünüme ilişkin raporlar
états = asv_config["états"]
# Aranan rapor, i numaralı görünüme ilişkin raporlar arasında mı?
if état in états:
trouvé = True
else:
# sonraki görünüm
i += 1
# bulundu mu?
if trouvé:
# Bu bir eylemdir ASV - şablonu oturumda zaten mevcut olan bir görünüm görüntülenmelidir
# yanıtın HTML kodunu oluşturuyoruz
html = render_template(asv_config["view_name"], modèle=session['modèle'])
# HTTP yanıtı oluşturulur
response = make_response(html)
response.headers['Content-Type'] = 'text/html; charset=utf-8'
# sonuç döndürülür
return response, status_code
# bulunamadı - bu, bir eylemin durum kodudur ADS
# bunun ardından bir yönlendirme gerçekleşecek
redirected = False
for ads in config['mvc']['ads']:
# yönlendirme gerektiren durumlar
états = ads["états"]
if état in états:
# yönlendirme var
redirected = True
break
# beklenmedik hatalar için yönlendirme sözlüğü
if not redirected:
ads = config['mvc']['view_erreurs']
# bu, ASD veya ASV eylemine bir yönlendirme mi?
# eğer bir şablon varsa, bu durumda ASV eylemine yönlendirmedir
# bu durumda, ASV eylemi tarafından görüntülenecek V görünümünün şablonu hesaplanmalıdır
model_for_view = ads.get("model_for_view")
if model_for_view:
# bir sonraki görünümün şablonunun hesaplanması
modèle = model_for_view.get_model_for_view(request, session, config, résultat)
# Şablon, bir sonraki görünüm için oturuma eklenir
session['modèle'] = modèle
# şimdi, istenirse CSRF jetonunu da unutmadan URL yönlendirme kodunu oluşturmak gerekiyor
if config['parameters']['with_csrftoken']:
csrf_token = f"/{generate_csrf()}"
else:
csrf_token = ""
# yönlendirme yanıtı
return redirect(f"{ads['to']}{csrf_token}"), status.HTTP_302_FOUND
- 18-35. satırlar: En son yürütülen eylemin ürettiği durumun bir ASV eylemine ait olup olmadığı kontrol edilir;
- satır 36-46: Eğer öyleyse, ASV eylemine ilişkin V görünümü, oturumda [‘modèle’] anahtarıyla ilişkili bulunan şablon kullanılarak görüntülenir;
- satır 48-60: buraya gelindiğinde, en son yürütülen eylemin ürettiği durumun ADS eylemine ait olduğu bilinir. Bu durumda bir yönlendirme gerçekleştirilir. Yapılandırma dosyasında bu yönlendirmenin tanımı aranır;
- 62. satır: Buraya geldiğimizde, yapılacak yönlendirmenin yapılandırmasına ulaşırız. İki durum söz konusudur:
- başka bir ADS eylemine yönlendirme söz konusuysa, hesaplanacak bir görünüm şablonu yoktur;
- ASV eylemine bir yönlendirme söz konusudur. Bu durumda hesaplanacak bir görünüm şablonu vardır (67-68. satırlar). Bu şablon daha sonra oturuma eklenir (70. satır);
- 72-76. satırlar: URL yönlendirme şablonu hesaplanır;
- satır 78-79: yönlendirme yanıtı istemciye gönderilir;
35.3. Testler
Bir tarayıcıyla aşağıdaki testleri yapın:
- uygulamayı normal şekilde kullanın. Tarayıcıda görüntülenen tek URL'lerin URL, ASV ve [/afficher-vue-nom_de_la_vue] olduğunu kontrol edin;
- sayfaları yenileyin (F5) ve aynı sayfanın yeniden görüntülendiğini kontrol edin. Herhangi bir yan etki yoktur;
Ayrıca, [impots/http-clients/09] istemcisini kullanın. Yapılan değişiklikler yalnızca HTML oturumlarını etkilediğinden, [main, main2, main3, Test1HttpClientDaoWithSession, Test2HttpClientDaoWithSession] istemcileri çalışmaya devam etmelidir.
Şimdi ise işlemin gerçekleştirilemediği bir durumu inceleyelim. Aşağıdaki görünüm görüntülenir:

[1] yerine URL [/supprimer-simulation/1] yazılır. [/supprimer-simulation] eylemi, görünümde önerilen 1-4 numaralı eylemler arasında yer almadığından reddedilecektir. Sunucunun yanıtı şöyledir:
