Skip to content

36. Uygulama alıştırması: sürüm 16

Image

36.1. Giriş

Uygulamamızdaki URL kodları şu anda [/action/param1/param2/…] biçimindedir. Bu URL'lere bir önek eklemek istiyoruz. Örneğin, [/do] önekini kullanırsak, URL'ler [/do/action/param1/param2/…] biçiminde olur.

36.2. Yolların yeni yapılandırması

Image

  • 'den [2]'e değiştirildiğinden, rota hesaplaması da değişecektir;
  • [1]'te, [config] dosyası bu değişikliği yansıtacak şekilde güncellenir;

[config] yapılandırması şu şekilde olur:


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))

    # yol yapılandırması
    import routes
    routes.configure(config)

    # yapılandırmayı geri yükleme
    return config
  • 37-39. satırlar: [routes] modülü (38. satır) rotaları yapılandırır (39. satır);

Jeton içermeyen rota dosyası CSRF [configs/routes_without_csrftoken] şu şekilde değişir:


# bağımlılıklar

from flask import redirect, request, session, url_for
from flask_api import status

# uygulama yapılandırması
config = {}

# ön denetleyici
def front_controller() -> tuple:
    # isteği ana denetleyiciye yönlendiriyoruz
    main_controller = config['mvc']['controllers']['main-controller']
    return main_controller.execute(request, session, config)

# uygulamanın kök dizini
def index() -> tuple:
    # /init-session/html adresine yönlendirme
    return redirect(url_for("init_session", type_response="html"), status.HTTP_302_FOUND)

# init-session
def init_session(type_response: str) -> tuple:
    # eylemle ilişkili denetleyici çalıştırılır
    return front_controller()

# kullanıcı-kimlik-doğrulama
def authentifier_utilisateur() -> tuple:
    # eyleme ilişkin denetleyici çalıştırılıyor
    return front_controller()

# vergi-hesapla
def calculer_impot() -> tuple:
    # eyleme ilişkin denetleyici çalıştırılır
    return front_controller()


Dosyadan rotalar kaldırılmıştır. Geriye sadece rotalarla ilişkili işlevler kalmıştır.

Jetonlu rota dosyaları CSRF ve [configs/routes_with_csrftoken] de aynı şekilde değiştirilmiştir:


# bağımlılıklar

from flask import redirect, request, session, url_for
from flask_api import status
from flask_wtf.csrf import generate_csrf

# yapılandırma
config = {}

# ön denetleyici
def front_controller() -> tuple:
    # istek ana denetleyiciye iletilir
    main_controller = config['mvc']['controllers']['main-controller']
    return main_controller.execute(request, session, config)

# uygulamanın kök dizini
def index() -> tuple:
    # /init-session/html adresine yönlendirme
    return redirect(url_for("init_session", type_response="html"csrf_token=generate_csrf()), status.HTTP_302_FOUND)

# init-session
def init_session(type_response: str, csrf_token: str) -> tuple:
    # eylemle ilişkili denetleyici çalıştırılıyor
    return front_controller()

# kullanıcı kimlik doğrulaması
def authentifier_utilisateur(csrf_token: str) -> tuple:
    # eylemle ilişkili denetleyici çalıştırılıyor
    return front_controller()



# json ve xml istemcileri için csrftoken olmadan oturum açma
def init_session_without_csrftoken(type_response: str) -> tuple:
    # /init-session/type_response adresine yönlendirme
    return redirect(url_for("init_session", type_response=type_response, csrf_token=generate_csrf()),
                    status.HTTP_302_FOUND)

Rotalar, aşağıdaki [configs/routes] modülü tarafından hesaplanmaktadır:


from flask import Flask

def configure(config: dict):
    # yol ayarları

    # Flask uygulaması
    app = Flask(__name__, template_folder="../flask/templates", static_folder="../flask/static")
    config['app'] = app

    # web uygulamasının rotalarının içe aktarılması
    if config['parameters']['with_csrftoken']:
        import routes_with_csrftoken as routes
    else:
        import routes_without_csrftoken as routes

    # yapılandırmayı rotalara enjekte ediyoruz
    routes.config = config

    # uygulamanın URL önekleri
    prefix_url = config["parameters"]["prefix_url"]

    # CSRF jetonu
    with_csrftoken = config["parameters"]['with_csrftoken']
    if with_csrftoken:
        csrftoken_param = f"/<string:csrf_token>"
    else:
        csrftoken_param = ""

    # Flask uygulamasının rotaları
    # uygulamanın kök dizini
    app.add_url_rule(f'{prefix_url}/', methods=['GET'],
                     view_func=routes.index)

    # init-session
    app.add_url_rule(f'{prefix_url}/init-session/<string:type_response>{csrftoken_param}', methods=['GET'],
                     view_func=routes.init_session)

    # csrftoken olmadan oturum açma
    if with_csrftoken:
        app.add_url_rule(f'{prefix_url}/init-session-without-csrftoken/<string:type_response>',
                         methods=['GET'],
                         view_func=routes.init_session_without_csrftoken)

    # kullanıcı-kimlik-doğrulama
    app.add_url_rule(f'{prefix_url}/authentifier-utilisateur{csrftoken_param}', methods=['POST'],
                     view_func=routes.authentifier_utilisateur)

    # vergi-hesaplama
    app.add_url_rule(f'{prefix_url}/calculer-impot{csrftoken_param}', methods=['POST'],
                     view_func=routes.calculer_impot)

    # toplu vergi hesaplama
    app.add_url_rule(f'{prefix_url}/calculer-impots{csrftoken_param}', methods=['POST'],
                     view_func=routes.calculer_impots)

    # simülasyonları listele
    app.add_url_rule(f'{prefix_url}/lister-simulations{csrftoken_param}', methods=['GET'],
                     view_func=routes.lister_simulations)

    # simülasyonu sil
    app.add_url_rule(f'{prefix_url}/supprimer-simulation/<int:numero>{csrftoken_param}', methods=['GET'],
                     view_func=routes.supprimer_simulation)

    # oturumu-sonlandır
    app.add_url_rule(f'{prefix_url}/fin-session{csrftoken_param}', methods=['GET'],
                     view_func=routes.fin_session)

    # vergi hesaplamasını görüntüle
    app.add_url_rule(f'{prefix_url}/afficher-calcul-impot{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_calcul_impot)

    # yönetici verilerini al
    app.add_url_rule(f'{prefix_url}/get-admindata{csrftoken_param}', methods=['GET'],
                     view_func=routes.get_admindata)

    # vergi-hesaplama-görünümünü-göster
    app.add_url_rule(f'{prefix_url}/afficher-vue-calcul-impot{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_calcul_impot)

    # kimlik-doğrulama-görünümünü-göster
    app.add_url_rule(f'{prefix_url}/afficher-vue-authentification{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_authentification)

    # simülasyon-listesi-görünümünü-göster
    app.add_url_rule(f'{prefix_url}/afficher-vue-liste-simulations{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_liste_simulations)

    # hata-listesi-görünümünü-göster
    app.add_url_rule(f'{prefix_url}/afficher-vue-liste-erreurs{csrftoken_param}', methods=['GET'],
                     view_func=routes.afficher_vue_liste_erreurs)

    # özel durum
    if with_csrftoken:
        # json ve xml istemcileri için csrftoken olmadan oturum açma
        app.add_url_rule(f'{prefix_url}/init-session-without-csrftoken', methods=['GET'],
                         view_func=routes.init_session_without_csrftoken)
  • 6-8. satırlar: Flask uygulaması oluşturulur ve yapılandırmaya eklenir;
  • 10-14. satırlar: duruma uygun rota dosyası içe aktarılır. İçe aktarılan dosyanın aslında rotalar içermediğini hatırlayalım. Dosya, yalnızca rotalarla ilişkili işlevleri içerir;
  • 17. satır: Rotalarla ilişkili fonksiyonların, uygulamanın yapılandırmasını bilmesi gerekir;
  • 20. satır: URL önekini not ediyoruz. Bu önek boş olabilir;
  • 22-27. satırlar: CSRF jetonuna sahip rotalar, jetonu olmayanlara göre ek bir parametreye sahiptir. Bu farkı yönetmek için [csrftoken_param] değişkeni kullanılır:
    • yollarda CSRF jetonu yoksa, bu değişken boş bir dize içerir;
    • CSRF jetonu varsa, değişken [/<string:csrf_token>] dizesini içerir;
  • 29-96. satırlar: Uygulamadaki her rota, 10-14. satırlarda içe aktarılan rota dosyasındaki bir işleve eşleştirilir;

36.3. Yeni denetleyiciler

Image

Önceki sürümde, tüm denetleyiciler işlenmekte olan eylemi şu şekilde alırdı:


    def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
        # yolun öğelerini alıyoruz
        params = request.path.split('/')
        action = params[1]

Örneğin [/do/lister-simulations] gibi bir önek varsa bu kod artık çalışmaz. Bu durumda, 3. satırdaki eylem [do] olur ve dolayısıyla hatalı olur.

Bu kodu şu şekilde dönüştürüyoruz:


    def execute(self, request: LocalProxy, session: LocalProxy, config: dict) -> (dict, int):
        # yolun öğeleri alınır
        prefix_url = config["parameters"]["prefix_url"]
        params = request.path[len(prefix_url):].split('/')
        action = params[1]

Bunu tüm denetleyicilerde yaparız.

36.4. Yeni şablonlar

Image

Önceki sürümde, her şablon sınıfı şu şekilde bir şablon oluşturuyordu:


def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # sayfa verilerini şablona yerleştirme
        modèle = {}
        # uygulamanın durumu
        état = résultat["état"]
        # şablon, duruma bağlıdır
        

        # CSRF jetonu
        modèle['csrf_token'] = super().get_csrftoken(config)

        # görünümden gerçekleştirilebilecek eylemler
        modèle['actions_possibles'] = []

        # şablon görüntülenir
        return modèle

Artık şablonda ek bir anahtar olacak, yani URL öneki:


def get_model_for_view(self, request: Request, session: LocalProxy, config: dict, résultat: dict) -> dict:
        # sayfa verilerini modele kapsülleme
        modèle = {}
        # uygulamanın durumu
        état = résultat["état"]
        # şablon duruma bağlıdır
        

        # CSRF jetonu
        modèle['csrf_token'] = super().get_csrftoken(config)

        # görünümden gerçekleştirilebilecek eylemler
        modèle['actions_possibles'] = []
    
        # URL önekleri
        modèle["prefix_url"] = config["parameters"]["prefix_url"]

        # şablonu geri gönderir
        return modèle

36.5. Yeni parçalar

Image

URL içeren tüm parçalar değiştirilmelidir.

[v-authentification] parçası


<!-- HTML formu - değerleri [authentifier-utilisateur] eylemiyle gönderilir -->
<form method="post" action="{{modèle.prefix_url}}/authentifier-utilisateur{{modèle.csrf_token}}">

    <!-- başlık -->
    <div class="alert alert-primary" role="alert">
        <h4>Veuillez vous authentifier</h4>
    </div>


</form>

[v-calcul-impot] parçası


<!-- gönderilen HTML formu -->
<form method="post" action="{{modèle.prefix_url}}/calculer-impot{{modèle.csrf_token}}">
    <!-- mavi arka planlı 12 sütunlu mesaj -->
    

</form>

[v-liste-simulations] parçası




{% if modèle.simulations is defined and modèle.simulations|length!=0 %}


<!-- simülasyon tablosu -->
<table class="table table-sm table-hover table-striped">
    
    <tr>
        <th scope="row">{{simulation.id}}</th>
        <td>{{simulation.marié}}</td>
        <td>{{simulation.enfants}}</td>
        <td>{{simulation.salaire}}</td>
        <td>{{simulation.impôt}}</td>
        <td>{{simulation.surcôte}}</td>
        <td>{{simulation.décôte}}</td>
        <td>{{simulation.réduction}}</td>
        <td>{{simulation.taux}}</td>
        <td><a href="{{modèle.prefix_url}}/supprimer-simulation/{{simulation.id}}{{modèle.csrf_token}}">Supprimer</a></td>
    </tr>
    {% endfor %}
    </tr>
    </tbody>
</table>
{% endif %}

[v-menu] parçası


<!-- Bootstrap menüsü -->
<nav class="nav flex-column">
    <!-- bağlantı listesinin görüntülenmesi HTML -->
    {% for optionMenu in modèle.optionsMenu %}
    <a class="nav-link" href="{{modèle.prefix_url}}{{optionMenu.url}}{{modèle.csrf_token}}">{{optionMenu.text}}</a>
    {% endfor %}
</nav>

36.6. Yeni yanıt HTML

Image

Önceki sürümde, HTML yanıt kodunun sonunda bir yönlendirme vardı:


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"]

        

        # şimdi, istenirse CSRF jetonunu da ekleyerek 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

Bu kod artık şu şekilde:


def build_http_response(self, request: LocalProxy, session: LocalProxy, config: dict, status_code: int,
                            résultat: dict) -> (Response, int):
        

        # Şimdi, istenirse CSRF jetonunu da unutmadan URL yönlendirme kodunu oluşturmalıyız
        if config['parameters']['with_csrftoken']:
            csrf_token = f"/{generate_csrf()}"
        else:
            csrf_token = ""

        # yönlendirme yanıtı
        return redirect(f"{config['parameters']['prefix_url']}{ads['to']}{csrf_token}"), status.HTTP_302_FOUND

36.7. Testler

Image

[parameters] yapılandırma dosyasına şu önek ekleyelim:



        # CSRF jetonu
        "with_csrftoken"True,
        # yönetilen veritabanları: MySQL (mysql), PostgreSQL (pgres)
        "databases"["mysql""pgres"],
        # uygulamanın URL önekleri
        # Önek istenmiyorsa dizgiyi boş bırakın, aksi takdirde /önek değerini girin
        "prefix_url""/do",

Uygulamayı çalıştıralım ve ardından URL [http://localhost:5000/do] sorgusunu gönderelim; yanıt şu şekildedir:

Image

  • [1]'te, URL'in öneki;
  • [2]'te, CSRF jetonu;

Uygulama, [http-clients/09] konsol testleriyle de test edilebilir:

Image

[1] ve [2] yapılandırma dosyalarında, URL önekini sunucunun URL dosyasına eklemeniz gerekir:


        "server": {
            # "urlServer": "http://127.0.0.1:5000",
            "urlServer""http://127.0.0.1:5000/do",
            "user": {
                "login""admin",
                "password""admin"
            },
            "url_services": {
                
            }
  • 3. satır: Sunucudaki URL artık URL'in önekini içermektedir;

Bu değişiklik yapıldıktan sonra, tüm konsol testleri çalışmalıdır.