Kümnes praktikum - Mikroteenused II - Veebiliidese loomine
Selles praktikumis loome juurde veel ühe mikroteenuse rakenduse kasutaja liidese jaoks (front-end) ja käivitame loodud mikroteenuse põhise rakenduse dockeri konteineritena.
Viited
- Docker'i ülevaade - https://docs.docker.com/get-started/overview/
- Docker käsurea käskude ülevaade - https://docs.docker.com/engine/reference/commandline/cli/
- Dockerfaili spetsifikatsioon - https://docs.docker.com/engine/reference/builder/
Probleemide korral kontrollige:
- Võimalikud probleemid ja nende potentsiaalsed lahendused osa praktikumi juhendi lõpus.
- Küsige otse
#praktikum-10-front
Zulip teemas.- Ka siis kui soovite lihtsalt viheid.
Ülesanne 10.1: Mikroteenuste modifitseerimine
Selles ülesandes muudame natukene eelmises praktikumis konteineriseeritud mikroteenuseid:
- hs9-flask-api-raamatud
- hs9-flask-api-raamatute-otsing
Peame konfigureerima Flask lubama sissetulevaid ühendusi, mis tulevat teistelt aadressidelt ja portidelt, et saaks API päringuid välja kutsuda JavaScipt kaudu. Tavaliselt on keelatud Javascript'i välja kutsumine, mis võtab ühendust kolmandate serveritega, et vältida Cross-Origin Resource Sharing (CORS) turvaprobleeme.
- Muudame eelmise praktikumi Mikroteenuste koodi.
- Tehke järgmsed muudatused mõlemas programmis:
- requirements.txt faili peame lisama veel ühe teegi:
flask-cors
- flask-cors'i kasutame selleks, et lubada API väljakutsumist teistest mikroteenustest hiljem, kui loome ka veebiliidese oma API'le.
- Peame sisse viima ka mõned muudatused Flask rakendusets:
- Lisage import käsk
from flask_cors import CORS
- Lisage flask-cors teegi konfiguratsioon programmi algusesse peale Flask app objekti loomist:
app = Flask(__name__) cors = CORS(app, resources={r"/raamatud/*": {"origins": "*"}, r"/raamatu_otsing/*": {"origins": "*"}})
- See lubab APIt välja kutsuda läbi teiste (javaScript) rakenduste, mis muidu keelatakse ära brauserite poolt (Cross Origin Resource Sharing limitatsioonide tõttu). Lisainfot selle kohta leiate siit: https://flask-cors.readthedocs.io/en/latest/
- Lisage import käsk
- requirements.txt faili peame lisama veel ühe teegi:
- Muutke API programme selliselt:
- Kontrollige, et raamatutest sõne otsingu meetodi
raamatutest_sone_otsimine
otspunkt lõppeb/
märgiga. - Peaks olema selliselt:
@app.route('/raamatu_otsing/', methods = ['POST']) def raamatutest_sone_otsimine():
- Muidu võib tekkida lahkheli veebiliidese ja API vahel ja saab CORS veateate
- Tehke sama ka järgmistemeetodite puhul:
@app.route('/raamatud/', methods = ['POST']) def raamatu_lisamine():
@app.route('/raamatud/', methods = ['GET']) def raamatu_nimekiri():
- Kontrollige, et raamatutest sõne otsingu meetodi
Ülesanne 10.2: Kasutajaliidese loomine
Selles ülesandes loome kolmanda mikroteenuse.
Selleks om HTML-põhise kasutajaliides meie veebiteenuse API mikroteenustele, mis kasutab JavaScript'i, et päringuid saata meie kahe API vastu. Samuti seame selle rakenduse üles eraldi Docker konteinerina.
- Looge uus projekt/kaust
hs10-front-end
. Saate vabalt valida failiredaktori (näiteks notepad++) või mõne HTML/JS IDE. - Loome sinna kaks faili:
front-end.html
- Sisaldab meie veebiliidese HTML koodi ja impodib JavaScrip faili- Faili sisu võtke siit: https://raw.githubusercontent.com/pjakovits/wsds/main/p09/front-end.html
apiparingud.js
- Sisaldab JavaScript koodi, mis suhtleb meie mikroteenustega.- Esialgne faili sisu võtke siit: https://raw.githubusercontent.com/pjakovits/wsds/main/p09/apiparingud.js
front-end.html
Faili me ei muuda. Selles failis on kaks vormi:- Raamatu loomine
- Raamatust sõne otsimine
- Lisaks on seal HTML plokk, mille kaudu saab POST päringute tulemust näidata kasutajale
- Lisaks on seal HTML plokk raamatute nimekirja näitamiseks
- Lisaks imporditakse JavaScript fail:
<script type="text/javascript" src="apiparingud.js"></script>
- Lisaks pannakse eraldi scriptis tööle JavaScript meetod
listiraamatud()
, mis vastutab raamatute nimekirja pärimise eest API'lt, ning mille me implementeerime ise JavaScript failis hiljem. apiparingud.js
failis tuleb meil mõned muudatused teha.- Praegu sisaldab see ainult abimeetodeid POST, GET ja DELETE meetodite välja kutsumiseks.
- Lisaks on POST päringu välja kutsumise JavaScript meetod
handleFormSubmit
loodud kavalalt.- Kui see lisada HTML POST vormi kuulama, siis saab selle abil automaatselt saata vormi
target
aadressile POST päringu, millega pannakse kaasa JSON dokument, mis sisaldab kõiki selle HTML vormiinput
tüüpi sisendeid. - Samuti saab see JavaScript meetod kätte REST POST päringu vastuse JSON dokumendina.
- Ning seda ilma, et kasutaja brauseri kaudu edasi suunataks API aadressile. Mis võimaldab meil POST päringut taustal läbi viia kui kasutaja vajutab HTM vormi
submit
nupul - Mõlemale HTML vormi (raamatu loomine ning raamatutest otsimine)
submit
nupule onhandleFormSubmit
juba HTML failis lisatud
- Kui see lisada HTML POST vormi kuulama, siis saab selle abil automaatselt saata vormi
Veebiliidest saab testida avades HTML faili brauseri kaudu (Näiteks Chrome).
Pange jooksma ka mõlemad APId:
- hs9-flask-api-raamatud - Port 5000
- hs9-flask-api-raamatute-otsing - Port 5001
Veebiliidese dünaamiliseks muutmine:
- Raamatu loomine ja raamatutest otsimine olemasolevate HTML vormide kaudu juba töötab (kui API'd on käima pandud), aga hetkel ei ole veel loodud JavaSCript funktsioonid, mis tulemust meile veebilehel näitaks.
- Implementeerime JavaScript failis
listiraamatud()
meetodi sisu:- Kutsume meetodi alguses välja GET päringu meie raamatute API vastu, mis kuulab päringuid localhost peal pordil 5000:
const responseData = await getDataAsJson("http://localhost:5000/raamatud/");
- NB!
responseData
muutuja on JSON objekt, mille sees peaks olema REST API päringu vastus
- NB!
- Otsime üles HTML dokumendi elemendi ID väärtusega
raamatud_result
:const resultElement = document.getElementById("raamatud_result");
- Seame selle ploki väärtuseks tühja sõne:
resultElement.innerHTML = ""
- Loome tsükkli, mis iga vastuses tagastatud raamatu ID kohta lisab
raamatud_result
HTML plokki uue lingi, mille kaudus aab raamatut alla laadida API'st:resultElement.innerHTML = "" for (var raamat of responseData.raamatud){ resultElement.innerHTML += '<a href="http://localhost:5000/raamatud/'+raamat+'" download="'+raamat+'.txt" >' +raamat+".txt</a> " + '<a href="#" onclick="deleteObject(\'http://localhost:5000/raamatud/'+raamat+'\')" > [kustuta]</a>' + "<br />"; }
responseData.raamatud
on API vastuse JSON element, milles on raamatu ID'de list.- Lisaks raamatute alla tõmbamise lingile, lisatakse ka raamatu kustutamise link. Selles lingis on JavaScript
onClick
sündmuse kuulaja, mis lingile vajutamisel kutsub väljadeleteObject(raamatu_API_link)
meetodi, mis hoolitseb raamatu kustutamise eest.
- Kutsume meetodi alguses välja GET päringu meie raamatute API vastu, mis kuulab päringuid localhost peal pordil 5000:
NB! See näite kood eeldab, et Azure'sse salvestatud failidel ei ole ".txt" laiendit!
- Lahenduseks saate kas:
- Muuta JavaScript koodi, et ta eraldaks
.txt
failide nimede lõpust.- Näiteks saab raamatu faili nime jagada
split('.')
funktsiooni abil kaheks ning kasutada ainult esimest "poolt"
- Näiteks saab raamatu faili nime jagada
- Muuta Python API koodi, et ta ei lisaks
.txt
failide lõppu Azuresse üles laadides
- Muuta JavaScript koodi, et ta eraldaks
Testige nüüd, kas raamatute alla tõmbamine ja kustutamine töötab!
- Vigade korral saate uurida Flask API rakenduste/konteinerite väljundeid ning brauseri arenduskonsoolis (F12) näidatavaid JavaScript veateateid.
- Selleks, et näha kas raamat kustutati või loodi, tuleb leheküljele refresh teha.
- Parandame kustutamisege seotud olukorra sellega, et kutsume
listiraamatud()
meetodi välja JavaScript meetodideleteObject
lõpus- Lisage sinna rida:
listiraamatud();
- Lisage sinna rida:
- Parandame raamatu loomisega seotud olukorra:
- Looge uus javaScript meetod
handleResponse
:function handleResponse(form, responseData) { }
- Lisage
handleResponse
meetodi väljakutsehandleFormSubmit(event)
funktsiooni sisse, enne rida} catch (error) {
. - Lisage sinna rida:
handleResponse(form, responseData);
- Selle tulemusena käivitatakse
handleResponse
peale iga POST päringu tegemist (HTML vormi kaudu) ning saame sellese meedodisse kirjutada mis peaks juhtuma, kui saame POST päringu tulemuse kätte.
- Selle tulemusena käivitatakse
- Implementeerime
handleResponse
meetodi sisu:- Lisame funktsiooni algusesse koodi mis otsib HTML dokumendist üles elemendi, kuhu saame kirjutada infot kasutajale, selle kohta kas POST päring õnnestus (raamatu loomine või raamatutest otsimine):
- Lisage sinna:
const resultElement = document.getElementById("tulemus");
- Lisage sinna:
- Kui vormi (mis käivitati) id oli "frontform", siis loodi uus raamat. Siis kirjutame üle "tulemus" HTML ploki - paneme sinna vormi POST päringu JSON vastuse sisemise võtme "tulemus" väärtuse (API saadab sõnumi, et raamatu loomine õnnestus). Samuti uuendame raamatute nimekirja.
- Lisage:
if(form.id == "frontform"){ resultElement.innerHTML = responseData.tulemus; listiraamatud(); }
- Lisage:
- Kui vormi (mis käivitati) id oli "otsinguform", siis otsiti sõnet raamatute seest. Siis kirjutame üle "tulemus" HTML plokki - kirjutame sinna vormi POST päringu JSON vastuse sisemise võtme "tulemus väärtuse". Raamatute nimekirja ei ole vaja uuendada.
- Lisage:
if(form.id === "otsinguform"){ var output = "Sõne " + responseData.sone + " leiti järgmistest raamatutest: <br/>" for (var tulemus of responseData.tulemused) { output += "Raamat " + tulemus.raamatu_id + " - " + tulemus.leitud + " korda! <br/>"; } resultElement.innerHTML = output }
- Lisage:
- Lisame funktsiooni algusesse koodi mis otsib HTML dokumendist üles elemendi, kuhu saame kirjutada infot kasutajale, selle kohta kas POST päring õnnestus (raamatu loomine või raamatutest otsimine):
- Looge uus javaScript meetod
- Parandame kustutamisege seotud olukorra sellega, et kutsume
Ülesanne 10.3: Kasutajaliidese käivitamine konteineris.
Konteineriseerime meie poolt loodud kasutajaliidese.
- Looge samase kausta Dockerfile järgneva sisuga:
FROM nginx:alpine COPY apiparingud.js /usr/share/nginx/html/apiparingud.js COPY front-end.html /usr/share/nginx/html/index.html EXPOSE 80
- Ehitame Docker image
hs10-front-end
veebiliidese mikroteenuse jaoks:docker build -t hs10-front-end -f Dockerfile .
- Seda saab käima panna Dockeri konteinerina pordi 80 peal nii:
docker run -di -p 80:80 hs10-front-end
Testige, et õnnestub kõik kolm Docker conteinerit üles seada ning rakendust kasutada.
- hs10-front-end - port 80
- hs9-flask-api-raamatud - port 5000
- hs9-flask-api-raamatute-otsing - port 5001
Tulemus võiks olla midagi sellist:
Esitada: tehke ekraanivaated, mis näitavad et konteinerite jooksutamine õnnestus ning veebirakenduse kaudu õnnestub raamatuid luua, kustutada, nendest otsida ning neid alla laadida.
Boonus ülesanne: Veebiliidese muutmine, et see sisaldaks ka konkreetsest raamatust otsimise funktsionaalsust.
See ülesanne on täielikult iseseisev. Aga võite küsida vihjeid või abi vigade otsimisel.
Eesmärk:
- Täiendage HTML faili, et sialdaks ka HTML form elemente konreetsest raamatust otsingu läbi viimiseks
- Võib lisada ühe individuaalse HTML formi, või iga listitud raamatu juurde väikese form'i.
- Lisage ka HTML blokk) kuhu hiljem panna päringu vastus, sarnaselet tavalisele raamatu otsingule
- Täienedage javascript koodi, et läbi viia konkreetsest raamatust otsingu API kutse ja tulemuse lisamine HTML dokumenti.
Boonusülesande osas tuleb esitada:
- Kood, mis sisaldab ka Dockerfile'i
- Ekraanivaade veebiliidesest, mis näitab läbiviidud otsingu tulemust.
Lahenduse esitamine
Praktikumi lahendusena tuleb esitada:
- Veebiliidese mikroteenuse koodi projekti kaust koos Dockerfailiga
- Ärge pange kaasa Python venv kaustasid!
- Ekraanivaated ülesandest 10.3
Võimalikud probleemid ja nende potentiaalsed lahendused.
- Kui saate CORS veateate raamatute otsingu kohta:
- Kontrollige, et raamatutest sõne otsingu meetodi
raamatutest_sone_otsimine
otspunkt ei lõppe/
märgiga. - Peaks olema selliselt:
- Kontrollige, et raamatutest sõne otsingu meetodi
@app.route('/raamatu_otsing', methods = ['POST']) def raamatutest_sone_otsimine():
- Muidu võib tekkida lahkheli veebiliidese ja API vahel ja saab CORS veateate