Alkalmazások fejlesztése

2. előadás

Horváth Győző
Egyetemi adjunktus
1117 Budapest, Pázmány Péter sétány 1/c., 2.420-as szoba
Tel: (1) 372-2500/1816
horvath.gyozo@inf.elte.hu

Tartalomjegyzék

  • Szerveroldali webprogramozás
  • Koncepciók (kiszolgálás, MVC)
  • Protokollok
  • JavaScript a szerveroldalon
  • Express
  • HTML (CSS)
  • Statikus oldalak kiszolgálása
  • Dinamikus oldalak kiszolgálása

Szerveroldali webprogramozás

Kliens-szerver architektúra

Alkalmazásszerver koncepciók

Modell-Nézet-Vezérlő minta

Modell-Nézet-Vezérlő minta

Angolul: Model-View-Controller (MVC)

  • Modell: adatok és feldolgozási logika (adatbázis)
  • Nézet: a kimenet
  • Vezérlő: folyamatirányítás, kérés fogadása, feldolgozása

MVC keretrendszerek a szerveroldalon

  • Java: Spring, Struts2
  • .NET: ASP.NET MVC
  • Ruby: Ruby on Rails
  • Python: Django
  • PHP: Laravel, Codeigniter, Zend, Symfony2

HTTP protokoll

HTTP protokoll

  • Kérés-válasz alapú protokoll a kliens és szerver között
  • Mindig a kliens kezdeményez
  • Kliens: kérés
    • kérést küld a 80-as TCP portra
    • jellemzően böngésző (hivatkozások, formok)
  • Szerver: válasz
  • TCP/IP réteg feletti protokoll
  • W3C szabvány

HTTP kérés

METÓDUS ERŐFORRÁS VERZIÓ
FEJLÉC: ÉRTÉK
FEJLÉC: ÉRTÉK
FEJLÉC: ÉRTÉK

ÜZENETTEST (opcionális)

HTTP kérés példa

Minimális:

GET /index.html HTTP/1.1
Host: webprogramozas.inf.elte.hu

Konkrét:

GET / HTTP/1.1
Host: webprogramozas.inf.elte.hu
User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:19.0) Gecko/20100101 Firefox/19.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: hu-hu,hu;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: __utma=159741371.1255432553.1308299517.1308299517.1308299517.1; __utma=32143338.2145495546.1326532899.1361177845.1362134456.25; __utmz=32143338.1361177845.24.12.utmcsr=google|utmccn=(organic)|utmcmd=organic|utmctr=(not%20provided)
Connection: keep-alive

HTTP kérés metódusok

  • GET: Megadott erőforrás letöltése
  • POST: Feldolgozandó adat felküldése
  • HEAD: ld. GET, de csak a válasz fejléceket kéri le
  • PUT: Feltölt a megadott erőforrást
  • DELETE: Törli a megadott erőforrást
  • TRACE: Visszaküldi a kapott kérést
  • OPTIONS: A szerver által támogatott HTTP metódusok listája
  • CONNECT: Kérést transzparens tunnellé alakítja (SSL-hez kell)

HTTP válasz

VERZIÓ STÁTUSZKÓD INDOKLÁS
FEJLÉC: ÉRTÉK
FEJLÉC: ÉRTÉK
FEJLÉC: ÉRTÉK

ÜZENETTEST (opcionális)

HTTP válasz példa

HTTP/1.1 200 OK
Date: Wed, 03 Apr 2013 07:11:56 GMT
Server: Apache/2.2.10 (Linux/SUSE)
Last-Modified: Wed, 20 Feb 2013 08:39:44 GMT
ETag: "fe8438-6d6-4d623e65e9400"
Accept-Ranges: bytes
Content-Length: 1750
Content-Type: text/html

<!DOCTYPE html>
<html>
    ...
</html>

HTTP válasz státuszok

  • 1xx: Informatív (kérés megkapva)
  • 2xx: Siker (kérés megérkezett, elfogadva)
    • 200 OK
  • 3xx: Átirányítás (további műveletre van szükség)
  • 4xx: Kliens hiba (kérés hibás, nem teljesíthető)
    • 404 Not found
    • 404 Nem található
  • 5xx: Szerver hiba (nem tudja teljesíteni a kérést)
    • 500 Internal Server Error

HTTP eszközök

  • Network/Hálózat fül a böngészők webfejlesztő eszköztárán
  • Firefox Live HTTP Headers plugin

Node.js

Node.js

  • parancssori JavaScript
  • Google V8 motorra épül
  • Aszinkron feldolgozás (ld. később)
  • szerveroldali környezet a http modul segítségével
  • node parancs (interaktív shell)
  • node fájlnév

NPM

  • Node Package Manager
  • npm parancs
  • node_modules könyvtár
  • npm init: projektleíró fájl (package.json) készítése
  • npm install package: telepítés a projekten belülre
  • npm install -g package: globálisan elérhető programok telepítése
  • npm install package --save: telepítés + bejegyzés a projektleíró fájlba mint függőség
  • npm install package --save-dev: telepítés + fejlesztői függőség (pl. teszteléshez szükséges program)

Node.js modulrendszer

  • főprogram + modulok
  • CommonJS modulformátum
  • egy modul = egy fájl
  • require: importálás
  • module.exports: exportálás
//Modul: math.js
function add(a, b) {
    return a + b;
}

module.exports = add;
//Főprogram: index.js
var add = require('./math');

console.log(add(2, 3));

Több elem exportálása

// math.js
module.exports = {
    add:      function (a, b) { return a + b; },
    multiply: function (a, b) { return a * b; },
};
// index.js
var math = require('./math');

console.log(math.add(2, 3));

// VAGY
var multiply = require('./math').multiply;

console.log(multiply(2, 3));

Express

Telepítés

  • npm install express --save
  • npm install express-generator -g (haladó)

http module

var http = require('http');

var port = process.env.PORT || 3000;
http.createServer(function (req, res) {
  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end('Hello browser!\n');
}).listen(port);

Express minimál

var express = require('express');
var app = express();

var port = process.env.PORT || 3000;
app.listen(port);

Express indításkori callback

var express = require('express');
var app = express();

var port = process.env.PORT || 3000;
var server = app.listen(port, function () {
  console.log('Server is running!');
});

Express http modullal

var express = require('express');
var http = require('http');

var app = express();

var port = process.env.PORT || 3000;
http.createServer(app).listen(port);

Express koncepciók

  • HTTP kérés: request objektum
    • URI
    • metódus
    • (adatok)
    • fejlécek
  • --> HTTP válasz: response objektum
    • status
    • mime-type
    • tartalom
    • fejlécek
  • a feldolgozást middleware-ek és útvonalkezelők végzik el

Middleware

A middleware egy függvény, amely hozzáfér a

  • kérés objektumhoz (request)
  • válasz objektumhoz (response)
  • a következő middleware-hez (next)
function (request, response, next) {

}

Middleware

  • Bármilyen kódot futtathat.
  • Megváltoztathatja a kérés és válasz objektumot.
  • Véget vethet a kérés-válasz folyamatnak.
  • Meghívhatja a következő middleware-t.
function (request, response, next) {
    //tetszőleges kód
    //request, response olvasása/írása
    next();
}

Middleware-ek használata

//Általános formája
app.use([path], middleware [, middleware]);

//Minden útvonalra lefut
app.use(function (req, res, next) {
    console.log('Middleware');
    next();
});

//Minden '/users' résszel kezdődő útvonalra lefut
app.use('/users', function (req, res, next) { /*...*/ });

//Példa végpont
app.use(function (req, res, next) {
    console.log('Végpont');
});

Express alkalmazás

Middleware-ek sorozata végén végponttal:

middleware
middleware-->middleware
middleware
middleware
middleware-->végpont

Routing (útvonalkezelés)

Az adott URI végpontnak az útvonalkezelőhöz (vezérlő) való hozzárendelése.

Végpontok:

http://expressjs.com/guide/routing.html
http://localhost:8080/snippets/3

Route

  • URI (path)
  • HTTP metódus (METHOD)
  • egy vagy több útvonalkezelő (handler)
//Általánosan
app.METHOD(path, handler [, handler ...]);
app.METHOD(path [, middleware, ...] handler);

//Például
app.get('/alma', function (req, res) {
    //kód
});

Route metódusok

  • get
  • post
  • put
  • delete
  • options
  • stb.
  • all

Teljes lista

URI útvonalak

//szöveg
app.get('/alma', function (req, res) { /* ... */ });
app.get('/public/index.html', function (req, res) { /* ... */ });

//szövegminta
app.get('/ab?cd', ...);     //acd vagy abcd
app.get('/ab+cd', ...);     //abcd, abbcd, abbbcd, ...
app.get('/ab*cd', ...);     //abcd, ab1cd, abVALAMIcd
app.get('/ab(cd)?e', ...);  //abe, abcde

//reguláris kifejezés
app.get(/alma/, ...);       //alma
app.get(/.*alma$/, ...);    //alma, szalma, pirosalma

Kezelők

  • Tulajdonképpen tetszőleges middleware
  • Általában a next() hívás nélkül (folyamatvég)
app.get('/', function (req, res) { /*...*/ });
app.get('/', function (req, res, next) {
    console.log('middleware');
    next();
}, function (req, res) {
    console.log('vege');
})
app.get('/', middleware1, middleware2, middleware3);
app.get('/', [middleware1, middleware2, middleware3]);

Middleware típusok

//Alkalmazás szintű
app.use(...);

//Router szintű
var router = express.Router();
router.use(...)

//Végpont szintű middleware-ek
app.get('/alma', middleware1, middleware2, middleware3);
app.use('/alma', middleware1, middleware2, middleware3);
router.get('/alma', middleware1, middleware2, middleware3);

//hibakezelő
app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Szerveroldali hiba!');
});

//plugin
app.use(express.static('public'));
app.use(cookieParser());

route() metódus

app.get ('/alma', function (req, res) { /*...*/ });
app.post('/alma', function (req, res) { /*...*/ });
app.put ('/alma', function (req, res) { /*...*/ });

//helyett

app.route('/alma')
    .get (function (req, res) { /*...*/ })
    .post(function (req, res) { /*...*/ })
    .put (function (req, res) { /*...*/ });

Router

  • moduláris útvonalkezelés
  • izolált
  • teljes middleware és végpontkezelő funkcionalitás
  • mini-app
  • vagy: app van kibővítve Router funkcionalitással
  • maga is middleware

Router definiálása és használata

//alma.js
//Definiálás
var express = require('express');
var router = express.Router();

// middleware csak ehhez a routerhez
router.use(function (req, res, next) { /*...*/ next(); });

// végpontok a routerhez
router.get('/', function(req, res) { /*...*/ });
router.get('/alma', function(req, res) { /*...*/ });

module.exports = router;
//Használat
var alma = require('./alma')

// /public és /public/alma 
app.use('/public', alma);

Kérés objektum

  • req.path
  • req.ip
  • req.get()
  • req.params
  • req.body

Válasz objektum

  • res.download(): fájl letöltésre
  • res.end(): válasz vége
  • res.json(): JSON válasz
  • res.jsonp(): JSON válasz JSONP formátumban
  • res.redirect(): átirányítás
  • res.render(): nézetsablon megjelenítése
  • res.send(): különböző típusú válaszok
  • res.sendFile(): fájl küldése

Response dokumentáció

HTML

HTML

Példa HTML dokumentum

<!DOCTYPE html>
<html>
    <head>
        <title>Oldalcím</title>
    </head>
    <body>
        <h1>1. címsor</h1>
        <p>Ez egy bekezdés</p>
        <img src="alma.jpg">
        <form>
            <input type="text" name="nev"> <br>
            <input type="submit">
        </form>
    </body>
</html>

CSS

  • HTML dokumentumok megjelenése
  • Stíluslapok
  • Szelektorok és szabályok
  • Források:
p { color: #123456; }
p.info {
    border: 1px dashed black;
    background-color: orange;
}
#main { position: relative; }
nav ul > li { padding-top: 10px; }

CSS keretrendszer

Statikus kiszolgálás

Kész megoldás

npm install http-server -g

http-server

express.static middleware

  • express.static(root [, options])
  • Leírás
app.use(express.static('public'));

Sablonok

Sablonmotor beállítása

  • ejs
  • jade
  • hbs
  • hogan.js
  • dust.js
app.set('views', './views');
app.set('view engine', 'hbs')

Handlebars telepítése

npm install hbs --save

Handlebars leírás

Elnevezések

  • Template: sablon, keret a változó részeket megfelelően jelölve
  • Layout: a globális elrendezése az oldalnak
  • Partials: kisebb, újrahasznosítható oldaltöredékek

Layout

layout.hbs

<!doctype html>
<html>
    {{>head}} //partial betöltése
    <body>
        <div class="container">
            {{{ body }}}  //body is placed
        </div>
    </body>
</html>

Layout megváltoztatása:

//Per view
res.render('view', { title: 'my other page', layout: 'other' });

//Globális
app.set('view options', { layout: 'other' });

<!--

//--------------------------

Partial

-->

Kifejezések

<h1>{{title}}</h1>
<h1>{{article.title}}</h1>

<!-- Nincs HTML escape-elés -->
{{{foo}}}

Iterálás

<div class="comments">
  {{#each comments}}
    <div class="comment">
      <h2>{{subject}}</h2>
      {{{body}}}
    </div>
  {{/each}}
</div>

{{#each paragraphs}}
  <p>{{this}}</p>
{{else}}
  <p class="empty">No content</p>
{{/each}}

Feltételek

{{#if isActive}}
  <img src="star.gif" alt="Active">
{{else}}
  <img src="cry.gif" alt="Inactive">
{{/if}}

{{#if isActive}}
  <img src="star.gif" alt="Active">
{{else if isInactive}}
  <img src="cry.gif" alt="Inactive">
{{/if}}

Feladatok

Feladatok

  1. Nézd meg a webprogramozas.inf.elte.hu HTTP kérését és válaszát a böngésződ webfejlesztő eszköztára segítségével!
  2. Készíts Node.js modult az egyes programozási tételeknek megfelelően! Az elkészült modult próbáld is ki egy node.js alkalmazás segítségével!
  3. Készíts egy Fibonacci modult, amely segítségével lekérdezhető az n. Fibonacci szám, illetve visszakapható az első n Fibonacci szám is!

Feladatok

  1. Készíts egy Express middleware-t, amely logolja a kérés metódusát és az útvonalat!
  2. Készíts egy Express middleware-t, amely a /secret útvonal esetén bővíti a kérésobjektumot egy titkos adattal, amit a válaszban visszaküldünk a kliensnek!
  3. Készíts egy Express alkalmazást, amely a /readme.txt útvonalra visszaad egy ilyen nevű fájlt!

Feladatok

  1. Készíts Express alkalmazást, mely statikus fájlkiszolgálóként működik! Írj bele néhány HTML fájlt, és a megjelenítést Bootstrap CSS keretrendszerrel végezd el!
  2. Készíts Express alkalmazást, amely dinamikusan állítja elő az oldalak tartalmát a Handlebars sablonmotor segítségével! Definiálj egy layout-ot és pár oldalt!