Web aplikacije

Web aplikacija je softverski program koji se izvršava na web (http) serveru. Za razliku od tradicionalnih, desktop aplikacija koje se pokreću vašim operativnim sistemom, web aplikacije se najčešće pokreću uz pomoć “web browser” programa (Chrome, Firefox, Opera, itd...).

Web aplikacije imaju više prednosti nad desktop aplikacijama. Pošto se izvršavaju na brauzerima ne moraju se prilagođavati različitim operativnim sistemima, ista aplikacija radi na svim platformama. Web aplikacije ne moraju da se distribuiraju i instaliraju na svakom računaru na koje su potrebe. One se instaliraju samo na jednom serveru kojem mogu da pristupaju svi korisnici aplikacije. Na taj način korisnici uvijek izvršavaju poslednju na serveru instaliranu varijantu aplikacije. Web aplikacije mogu biti distupne na svim hardverskim uređajima: desktopovima, laptopovima, tabletima, smart telefonima, kao i svim drugim uređajima koji se mogu povezati na internet putem http protokola.

U daljem tekstu ćemo se upoznati sa bazičnim tehnologijama koje se koriste pri razoju web aplikacija.

Komponente web aplikacije

Sledeća slika prikazuje arhitekturu (strukturu) moderno dizajniranih web aplikacija.

_images/web-stack.png

Na klijentskoj strani se koriste tri osnovne tehnologije:

  • CSS
  • HTML
  • JavaScript

JavaScript ima višestruku ulogu:

  • Za programiranje komunikacije sa serverom
  • Kao pomoć za dinamičke promene u UI
  • Kao realizaciju dela biznis logike

HTML ima jednu jedinu ulogu:

  • Definiše layout komponenti u UI

CSS takođe ime samo jednu ulogu:

  • Definiše način prikaza komponenti UI

Na serverskoj strani se najčeće koriste dve tehnologije:

  • Programski jezik za programniranje komunikacije sa klijentom i realizaciju biznis logike
  • Baza podataka za permanentno skaldište podataka (perzistenciju)

Aplikacija na jednoj stranici (SPA)

Sledeća slika prikazuje tradicionalan i moderan način komunikacije između servera i klijenta.

_images/spa.png

U tradicionalnom načinu, svaki put kada se klijent obrati serveru ovaj mu isporuči čitavu stranicu sa svim sadržajima.

U modernom prustupu, server samo jedanput, na početku, isporuči klijentu osnovnu stranicu ( obično index.html ). Kada klijent pravi dalje zahteve server mu šalje samo tražene podatke ( Ajax komunikacija ), a ne cele stranice kao u tradicionalnom slučaju. Na taj način se postiže mnogo veća brzina rada aplikacije ( responsive applications ).

Primer web aplikacije

Sada ćemo analizirati jednu takvu osnovnu aplikaciju koja može da nam posluži kao okvir (framework) za buduće aplikacije (projekte).

Namena ove aplikacije je da nam demonstrira jedan način implementacije SPA (Single Page Application) modela.

Početna stranica sadrži ulazna polja (<input>) koja prihvataju metod, putanju i sadržaj Ajax zehteva. Podaci iz ovih polja će klikom na dugme “Pošalji zahtev” biti prosleđeni serveru, koji će nakon obrade zahteva vratiti odgovor sa istim tim podacima kao potvrdu da je primio i razumeo zahtev.

Naša aplikacija će se sastojati od četiri komponente:

  • Početne stranice (index.html)
  • Servera aplikacije (app.py)
  • JavaScript programa (app.js)
  • CSS fajla (app.css)

Jedino nedostaje baza podataka pa da imamo tzv. “full stack” aplikaciju.

Početna stranica (index.html)

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="description" content="Lekcija 2">
    <meta name="keywords" content="HTML,CSS,XML,JavaScript">
    <meta name="author" content="Milan Popovic">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Lekcija 2</title>
    <link rel="stylesheet" href="app.css">
    <script src="app.js"></script>
  </head>
  <body>
    <h1>Ajax zahtev klijenta</h1>
    <pre>Metod   : <input id="metod"   size=80></pre>
    <pre>Putanja : <input id="putanja" size=80></pre>
    <pre>Sadržaj : <input id="sadrzaj" size=80></pre>
    <button onclick=PosaljiAjaxZahtev()>Pošalji zahtev</button>
    <div id="odgovor"></div>
  </body>
</html>

CSS fajl (app.css)

button {
  background-color: green;
  color: white;
  font-size: large;
  font-weight: bold
}
h1 {
  color: blue;
  background-color: yellow;
  width:70%
}
h2 {
  color: red
}
body {
  margin-left: 20%
}
#odgovor {
  width:100%;
  color: blue
}

JavaScript program (app.js)

function AjaxZahtev(options, callback) {
  var req = new XMLHttpRequest();
  req.open(options.metod, options.putanja, true);
  req.addEventListener("load", function() {
    if (req.status < 400) {
      callback(req.responseText);
    }
    else {
    callback(new Error("Request failed: " + req.statusText));
    }
  });
  req.addEventListener("error", function() {
    callback(new Error("Network error"));
  });
  req.send(options.sadrzaj || null);
}

function PosaljiAjaxZahtev(){
  var options = {}
  options.metod = document.getElementById("metod").value
  options.putanja  = document.getElementById("putanja").value
  options.sadrzaj = document.getElementById("sadrzaj").value
  AjaxZahtev(options, ProcesirajOdgovor)
}

function ProcesirajOdgovor(odgovor){
  document.getElementById("odgovor").innerHTML = "<h1>Odgovor servera</h1><pre>"+odgovor+"</pre>"
}

Python server aplikacije (server.py)

import http.server, os
BaseHandler = http.server.BaseHTTPRequestHandler
class Handler(BaseHandler):
    def _set_headers(self, type):
        self.send_response(200)
        self.send_header('Content-type', type)
        self.end_headers()
    def do_GET(self):
        filename = self.path.split("/")[-1]
        if filename == "" : filename = "index.html"
        if os.access(filename, os.R_OK) and not os.path.isdir(filename):
            ext = filename.split(".")[-1]                       # Klijent zahteva fajl
            mode = "r"
            if ext in ["html","htm"]: content_type = "text/html"
            elif ext in ["txt","js","py","php"]: content_type = "text/plain"
            elif ext in ["css"]: content_type = "text/css"
            elif ext in ["ico","jpg","jpeg","png","gif"]:
                content_type = "image/x-icon"
                mode = "rb"
            content = open(filename, mode).read()
            if mode == "r": content = str.encode(content)
            self._set_headers(content_type)
            self.wfile.write(content)
        else:                                 # Ajax zahtev
            odgovor = {"metod":"GET", "path": self.path, "sadrzaj": ""}
            self._set_headers("text/json")
            self.wfile.write(str.encode(str(odgovor)))
    def do_POST(self):
        putanja = self.path
        metod = self.command
        duzina_sadrzaja = int(self.headers['Content-Length'])
        sadrzaj = self.rfile.read(duzina_sadrzaja).decode("utf-8")
        odgovor = {"metod": metod, "putanja": putanja, "sadrzaj": sadrzaj}
        self._set_headers("text/json")
        self.wfile.write(str.encode(str(odgovor)))
try:
    httpd = http.server.HTTPServer(('',8888), Handler)
    print("Server startovan...port: 8888")
    httpd.serve_forever()
except:
    print("Server stopiran")

U celom ovom programu najinteresantniji deo su metode definisane u Handler klasi.

Metoda _set_headres služi da server pomoću nje pošalje klijentu potvrdu da je primio zahtev i tip ( format ) sadržaja koji će poslati klijentu.

Metode def_GET i def_POST su metode koje obrađuju zahteve klijenta.

GET i POST su dva osnovna zahteva koje klijent može da uputi serveru:

  • GET - Zahteva podatke od nekog serverovog resursa ( definisan pomoću URL adrese - putanje )
  • POST - Šalje serveru podatke koje će obraditi neki serverov resurs ( definisan pomoću URL adrese-putanje )

Za vežbu, analizirajte prikazane komponente, eksperimentišite sa promenama u komponentama. Krajnji cilj je da razumete mehanizam funkcionisanja (logiku) web aplikacije. Nemojte se plašiti ako dobro ne poznajete sintaksu jezika koji su korišćeni. O svakom od ovih jezika biće govora u narednim poglavljima.