V dnešnom blogu pre predstavu opíšem ako funguje klasický routing a uploadovanie súborov.
Časť 2: Ako funguje Total.js na pozadí
Ak Vás zaujala časť 1, tak určite Vás zaujme aj táto časť 2. V tejto časti sa pozrieme na spracovanie dynamického obsahu a na uploadovanie súborov.
Routing
Trasovanie (routing) považujem asi za najdôležitejšiu časť frameworku, pretože registrovaním (vytvorením) trasy vzniká webová aplikácia. Total.js pozná 3x rôzne typy trás:
- dynamický obsah:
F.route()
, príklad:/products/
- súbory:
F.file()
, príklad:/img/logo.png
- websocket:
F.websocket()
príklad:/chat/
Framework vyhodnocuje trasovanie pre každý typ requestu trošku inak. Pri prijatí requestu vyhodnotí framework, či sa jedná o súbor (kontroluje či má .extension
- koncovku) a ak platí, že sa jedná o súbor, tak request ďalej spracúva tzv. FileHandler - (spracovanie statických súborov), v opačnom prípade sa request začne analyzovať ako dynamický obsah:
- Total.js sa snaží vyparsovať z requestu tzv.
flags
napr.xhr
,mobile
,robot
,post
,put
, atď. - následne sa overí podľa
Content-Type
, či request obsahuje binárne data
Pokiaľ obsahuje request.method: GET, HEAD alebo OPTIONS
Tento typ requestu je spracovaný najrýchlejšie, pretože neobsahuje žiadne data v tele requestu, ktoré je potrebné dodatočne parsovať. Takže framework vyhodnotí:
- vyhodnotí sa CORS
- globálny middleware (ak je)
- vyhodnotí sa autorizácia (ak je)
- vyhodnotí sa trasovanie
- vyhodnotí sa route middleware (ak je)
- vytvorí sa inštancia controllera
- spustí sa akcia s scope controllera
Request s content-type: application/x-www-form-urlencoded alebo iným
Pri zistení, že sa jedná o tento typ requestu, tak framework vyhľadá trasu route
a podľa jej nastavenia začne načúvať udalosť request.on('data')
. Ak trasa neexistuje, framework vracia 403
a žiadne data nepríjma.
Ak trasa existuje tak do dočasnej premennej req.buffer_data
začne framework postupne zapisovať prijaté data po chunk
(rozumej pole bytes
), veľkosť prijatých dát (default: 5 kB) závisí od povolenia v metóde F.route()
(túto veľkosť je možné zmeniť celoplošne aj v configu). Ako náhle veľkosť prekročí stanovený limit:
- framework ruší príjmanie dát a maže obsah
req.buffer_data
pre vyčistenie pamäti - následne je clientovi vrátená chyba
431: Request Header Fields Too Large
V prípade, že data sú OK, tak sa vyhodnocuje:
- data sa deserializujú (
JSON to Object
,UrlEncoded to Object
,Xml to Object
alebo ostávajúraw
) - ak trasa obsahuje mapovanie na schému, tak data sa predpripravia a validujú podľa schémy
- ďalej sa vykoná authorizácia (ak je)
- vyhodnotí route middleware (ak je)
- vytvorí sa inštancia controllera
- spustí sa akcia v rámci scope controllera
Request s content-type: multipart/form-data
Uploadovanie súborov je v Total.js dosť špecifické kvôli 2 veciam (o tom neskôr). Na spracovanie uploadu používam algoritmus node-formidable, ktorý používa ak sa nemýlim aj framework Express.js. V každom prípade, nejdem písať o tom ako funguje tento algoritmus, ale ako Total.js rieši problémy s uploadovaním súborov a čo všetko k tomu ešte navyše ponúka.
Upload súborov funguje tak, že všetky súbory v requeste sa automaticky ukladajú do temporary adresára /myapp/tmp/
. Framework pri zisťovaní obsahu requestu a zistení, že sa jedná o súbor otvorí Writable Stream
pre každý súbor a prijaté data po chunk
(rozumej pole bytes
) zapisuje do súboru. Celý tento proces je streamovaný a neblokuje vlákno. V prípade, že sa jedná o obrázok, tak framework sa snaží na základe content-type
súboru zistiť jeho rozmery v prvom chunk
. Pri gif
, png
a svg
je rýchlo čitateľné rozlíšenie obrázku, no a pri jpg
je ten proces trošku náročnejší. Takže prvou špecifickou vecou v Total.js je to, že Total.js Vám pri uploade obrázkov ihneď zistí ich rozlíšenie (toto správanie sa dá vypnúť cez tzv. Total.js behaviour
). Ostatné data (nie binárne) sú spracované klasicky ako pri content-type application/x-www-form-urlencoded
.
Druhou špecifickou vecou pri uploade súborov je to, že Total.js počíta veľkosť aktuálne zapísaných dát, takže sa nemôže stať, že Vám niekto začne uploadovať 1 GB data a tým zaťažovať server. By default je veľkosť nastavená na 5 kB
a pri každej F.route()
si viete túto veľkosť zmeniť:
Po dokončení príjmania dát sa:
- data sa deserializujú (
UrlEncoded to Object
) - ak trasa obsahuje mapovanie na schému, tak data sa predpripravia a validujú podľa schémy
- ďalej sa vykoná authorizácia (ak je)
- vyhodnotí route middleware (ak je)
- vytvorí sa inštancia controllera
- spustí sa akcia v rámci scope controllera
Framework vymaže všetky nahraté súbory automaticky, keď sa ukončí response
. Toto správanie sa dá tiež vypnúť cez controller.noClear(true)
, len potom je potrebné vymazať data manuálne cez controller.clear()
alebo req.clear()
.
WebSocket
O WebSocket budem písať v inom blogu, ktorý sa mu bude samostatne venovať. Len pre informovanosť uvediem, že spracovanie WebSocketu funguje trošku inak.
Zaujímavosti
Query argumenty:
req.query
/ controller.query
vlastnosť je parsovaná len vtedy, keď sa volá. Výsledky parsera sú následne uložené do skrytej property a pri opätovnom volaní sa už hodnoty znova neparsujú.
Vyhodnotenie trás je cacheované:
Vyhodnotenie trás a ich flagov sa cachuje. To znamená, že framework sa snaží zapamätať zapamätateľné trasy, tak - aby pri nasledujúcom requeste nemusel prehľadávať všetky trasy znova.
CORS:
CORS mechanizmus sa vyhodnocuje ihneď v prípade OPTIONS
metódy a ďalej framework vo vyhodnocovaní nepokračuje. Napísal som ho tak, aby bol čo najefektívnejší. Framework obsahuje F.cors()
metódu na povolenie CORS, viac v dokumentácii.
Flagy robot
a referer
:
Sú analyzované len vtedy, ak framework obsahuje reálne routes
s týmito flagmi. ... aj takto som sa snažil šetriť performance.
Timeouty:
Zaujímavou funkciou v Total.js sú timeouty. Pokiaľ controller neodpovie v požadovanom čase, napr. stane sa neočakávaná chyba, framework jednoducho request ukončí. Toto správanie sa dá vypnúť alebo upraviť jeho čas priamo pri vytváraní trasy F.route()
. Viac info v dokumentácii.
Dynamicky definované argumenty:
Dynamicky definované argumenty v trasovaní sa nevyhodnocujú nijako špeciálne. Framework vidí dynamické hodnoty takto /products/{0}/{1}/{2}/
a my ich deklarujeme napr. takto /products/{category}/{subcategory}/{detail}/
takže v konečnom dôsledku ich v akcii môžeme nazvať ako chceme, viď nižšie uvedený príklad:
Other posts from Peter Širka
- 2024-02-18Ako lietať s long range dronom?
- 2024-02-09Recenzia drona DJI FPV
- 2024-02-07Recenzia drona DJI Avata
- 2023-01-06Ako spustiť Total.js framework?
- 2023-01-03Inštalácia Node.js + vysvetlenie použitia modulov
- 2022-05-26Základné informácie o Total.js Platforme
- 2021-01-15Je tu Total.js 4 - jeden z najlepších Node.js frameworkov
- 2020-02-12Total Avengers
- 2019-09-18JavaScript core pre Pie a Donut SVG grafy
- 2019-09-03Slovenská náhrada za Angular.js, React.js a Vue.js