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

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ı

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

Ö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

Ö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

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

Ö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

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

- [1]'te, URL'in öneki;
- [2]'te, CSRF jetonu;
Uygulama, [http-clients/09] konsol testleriyle de test edilebilir:

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