Skip to content

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:

Image

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:

Image

İş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:

Image

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:

Image

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

Image

[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

Image

[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

Image

[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

Image

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

Image

Ş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:

Image

  • 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

Image

[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

Image

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:

Image

[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:

Image