home_site

Lab11 - Node.js [ ver. TI.2025.12.06.004 ]

Zawartość strony

Plan zajęć

A. Node.js z linii poleceń

Celem laboratorium jest zapoznanie się z serwerem node.js i uruchamianie aplikacji w języku JavaScript po stronie serwera. W ramach pierwszego ćwiczenia zrealizujemy polecenia z powłoki shell aplikacji node.js.

  1. Interaktywna praca w aplikacji node.js.
    $ node
    >
    
  2. Uruchomienie polecenia w języku JavaScript z linii poleceń node.js
    $ node
    > console.log("Hello node.js !");
    
  3. Uruchomienie pliku w języku JavaScript z linii poleceń node.js. Umieszczamy w pliku test.js polecenie z poprzedniengo przykładu i uruchamiamy plik.
    $ node test.js
    

B. Serwer WWW z wykorzystaniem node.js

  1. Prosty serwer WWW
    1. Pierwsza wersja serwera WWW.
       
      var http = require('http');
      var server = http.createServer(function (request, response) {
              response.writeHead(200, {"Content-Type": "text/plain"});
              response.write("Hello World");
              response.end();
      }).listen(<port>,"127.0.0.1");
      console.log('Server running at http://127.0.0.1:<port>/');
      
    2. Serwer WWW uruchamiamy na portach większych od 1024, każda osoba na innym porcie.
    3. Sprawdzenie poprawności działania serwisu: przeglądarka lub polecenie curl.
      $ curl -X GET http://localhost:<port>/
      
    4. Alternatywna wersja serwera WWW.
       
      // Creation and running of the Server
      var http = require('http');
      
      function SerwisWWW(request, response) {
        response.writeHead(200, {"Content-Type": "text/plain"});
        response.write("Hello Node.js");
        response.end();
      }
      
      var webserver = http.createServer(SerwisWWW).listen(<port>,"127.0.0.1");
      
      webserver.once('listening', function() {
              console.log('Server running at http://127.0.0.1:<port>/');
      });
      
  2. Przetwarzanie danych użytkownika - Request i Response
    1. Serwer

      Skrypt serwera - plik lab13_2.js ( [listing dokumentu] link do dokumentu )

         // add necessary modules 
      var http = require('http');
      var qs = require('querystring');
       
      // build a simple form
      var pageHTML = '<html>' +
        '<head>' +
          '<title>Add something</title>' +
          '<meta charset="utf-8">' +
        '</head>' + 
        '<body>' +
          '<form method="post" action="">' +
            '<div>' +
              '<label for="fname">First name:</label>' +
              '<input type="text" name="fname">' +  
            '</div>' +
            '<div>' +
              '<input type="submit" value="send it">' +
            '</div>' +
          '</form>' +
        '</body>' +
      '</html>';
       
      // create server and process data
      var server = http.createServer(function (req, res) {
        var requestData = '';
       
        // check HTTP method and show the right content
        if (req.method === "GET") {
          res.writeHead(200, {'Content-Type': 'text/html'});
          res.end(pageHTML); // serve our HTML code
        } else if (req.method === "POST") {
          req.setEncoding('utf-8');
       
          req.on('data', function(data) {
            requestData += data;
          });
       
          req.on('end', function() {
            var postData = qs.parse(requestData);
            res.writeHead(200, {'Content-Type': 'text/html'});
            res.end('<h1>Your nick: '+ postData.fname + '</h1>');
          });
        }
      });
      
      server.listen(<port>, '127.0.0.1');
      console.log('Server running at http://127.0.0.1:<port>/');
        

C. Framework Express do realizacji serwisu WWW w node.js

Tworzymy dwa pliki app.js (serwer) i routes.js (realizacja zadań serwisu).

  1. Skrypt serwera app.js
    var http = require('http');
    var express = require('express');
    var app = express();
    var routes = require('./routes')(app);
    http.createServer(app).listen(<port>, function(){
                    console.log('Express server listening on port ' + <port>);
    });
    
  2. Skrypt router'a routes.js
    module.exports = function(app) {
            app.get('/', function(req, res) {
                    // Send a plain text response
                    res.send('First Express Application!');
            });
            app.get('/hello', function(req, res) {
                    // Send a plain text response
                    res.send('Hello World!');
            });
    };
    
  3. Testowanie aplikacji z wykorzystaniem polecenia curl.
    $ node app.js &
    $ curl -X GET http://localhost:<port>/
    $ curl -X GET http://localhost:<port>/hello
    

D. Prosta aplikacja RESTful w node.js

Tworzymy dwa pliki app.js (serwer) i routes.js (realizacja zadań serwisu).

  1. Plik serwisu rest_server.js
    var express = require('express');
    var app = express();
    var fs = require("fs");
    
    app.get('/listUsers', function (req, res) {
       fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
           console.log( data );
           res.end( data );
       });
    })
    
    app.get('/:id', function (req, res) {
       // First read existing users.
       fs.readFile( __dirname + "/" + "users.json", 'utf8', function (err, data) {
           users = JSON.parse( data );
           var user = users["user" + req.params.id]
           console.log( user );
           res.end( JSON.stringify(user));
       });
    })
    
    var server = app.listen(<port>, function () {
      var host = server.address().address
      var port = server.address().port
      console.log("Example app listening at http://%s:%s", host, port)
    })
    
  2. Plik z danymi users.json
    {
       "user1" : {
          "name" : "Adam",
              "password" : "password1",
              "profession" : "student",
              "id": 1
       },
       "user2" : {
          "name" : "Marek",
              "password" : "password2",
              "profession" : "student",
              "id": 2
       },
       "user3" : {
          "name" : "Tadeusz",
              "password" : "password3",
              "profession" : "student",
              "id": 3
       }
    }
    
    

E. Aplikacja CRUD w node.js z wykorzystaniem frameworku express i bazy danych MongoDB

Do realizacji projektu wymagane jest zainstalowanie dwóch pakietów - mongodb i body-parser.

npm install body-parser
npm install mongodb@2.2.36
  1. Na początek utworzymy plik serwera server.js

    Plik server.js ( [listing dokumentu] link do dokumentu )

    const express = require('express');
    const bodyParser= require('body-parser')
    const mongodb = require('mongodb')
    var db
    const dbname = 'user';
    const url = 'mongodb://user:pass@172.20.44.30/user';
      
    const app = express();
    //app.use(bodyParser.json());
    //app.use(bodyParser.urlencoded({extended: true}))
    
    // create application/json parser
    const jsonParser = bodyParser.json()
    // create application/x-www-form-urlencoded parser
    const urlencodedParser = bodyParser.urlencoded()
      
    mongodb.MongoClient.connect(url, function(err, client) {
      if (err) return console.log(err)
      db = client.db(dbname);
      console.log('Connect OK');
    })
      
    app.listen(4100,function() {
       console.log('listening on 4100')
    })
      
    app.get('/', function(req,res) {
      res.send('Aplikacja CRUD - node.js')
    })
      
    app.get('/form', function(req,res) {
      res.sendFile(__dirname + '/form.html')
    })
      
    app.post('/stud', urlencodedParser, function( req,res ) {
       console.log(req.body)
       db.collection('stud').insertOne(req.body,function(err,result) {
          if (err) return console.log(err)
          console.log('Rekord dodany do bazy')
          res.end('{"inserted record":"'+result.insertedCount+'"}')
       })
    })
    
    app.post('/stud2', jsonParser, function( req,res ) {
       console.log(JSON.stringify(req.body))
       db.collection('stud').insertOne(req.body,function(err,result) {
          if (err) return console.log(err)
          console.log('Rekord dodany do bazy')
          res.end('{"inserted record":"'+result.insertedCount+'"}')
       })
    })
      
    app.get('/stud', function(req, res) {
      var cursor = db.collection('stud').find().toArray(function(err, results) {
         if (err) return console.log(err)
         res.end(JSON.stringify(results))
         console.log(results) 
      })
    })
     
    app.get('/stud/:id', function(req,res) {
       console.log(req.params.id)
       db.collection('stud').findOne({_id: new mongodb.ObjectId(req.params.id)},function(err,result) {
           if (err) return console.log(err)
           res.end(JSON.stringify(result))
           console.log(result)
       })      
    })  
  2. W kolejnym punkcie tworzymy formularz umożliwiający wprowadzanie danych form.html.

    Plik form.html ( [listing dokumentu] link do dokumentu )

    <form action="/stud" method="POST">
      <input type="text" placeholder="fname" name="fname">
      <input type="text" placeholder="lname" name="lname">
      <input type="text" placeholder="email" name="email">
      <button type="submit">Submit</button>
    </form>
      
  3. Testowanie aplikacji z wykorzystaniem polecenia curl.
    $ node server.js &
    $ curl -X GET http://localhost:<port>/
    $ curl -i -d 'fname=marek&lname=Cabacki&email=cabacki@o.pl' -X POST http://localhost:<port>/stud
    $ curl -i -H "Content-Type: application/json" -d '{"fname":"marek","lname":"Dabacki","email":"cabacki@o.pl"}' -X POST http://localhost:<port>/stud2
    $ curl -X GET http://localhost:<port>/stud
    

F. Technologia WebSocket w node.js

Tworzymy dwa pliki server_ws.js (serwer) i index.html (aplikacja klienta). Do realizacji zadania wymagana jest instalacja lokalnie pakietu ws.

npm install ws
  1. Zawartość pliku serwera server_ws.js

    Plik server_ws.js ( [listing dokumentu] link do dokumentu )

    const express = require('express');
    const http = require('http');
    const url = require('url');
    const WebSocket = require('ws');
     
    const app = express();
     
    app.use(function (req, res) {
      res.send({ msg: "hello" });
    });
     
    const server = http.createServer(app);
    const wss = new WebSocket.Server({ server });
     
    wss.on('connection', function connection(ws, req) {
      const location = url.parse(req.url, true);
    
      ws.on('message', function incoming(message) {
        console.log('received: %s', message);
        ws.send('test');
      });
     
      //ws.send('test');
    });
     
    server.listen(3001, function listening() {
      console.log('Listening on %d', server.address().port);
    });
      
  2. Tworzymy plik zawierający aplikację klienta index.html

    Plik index.html ( [listing dokumentu] link do dokumentu )

    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>Test aplikacji WebSocket echo</title>
        <script>
          var open = false
            , lastMessage
     
          window.onload = function () {
            // utwórz gniazdo
            var ws = new WebSocket('ws://pascal:3001');
            ws.onopen = function () {
              open = true;
            }
            ws.onmessage = function () {
              // odebrano komunikat zwrotny, zmierz opóźnienie
              console.log('Odebrano');
              dd = new Date - lastMessage;
              console.log(dd);
              document.getElementById('latency').innerHTML = new Date - lastMessage;
            }
     
            // zdefiniuj funkcje obsługi dla formularza
            document.getElementById('send').onsubmit = function () {
              if (open) {
                // zapisz stempel czasowy
                lastMessage = +new Date;
                console.log(lastMessage);
                // wyślij komunikat
                ws.send(document.getElementById('text').value);
                // wyczyść pole tekstowe
                document.getElementById('text').value = '';
                document.getElementById('text').focus();
              }
              return false;
            }
          }
        </script>
      </head>
      <body>
        <h1>WebSocket Echo</h1>
        <h2>Opóźnienie: <span id="latency"></span>ms</h2>
     
        <form id="send">
          <input type="text" id="text">
          <button>Wyślij</button>
        </form>
      </body>
    </html>
      

G. Technologia SSE

W ramach tego zadania utworzymy serwer wysyłający komunikaty SSE na serwerze node.js - server_sse.js. Dodatkowo zainstalujemy pakiet do osbługi CORS cors.

npm install --save cors
  1. Zawartość pliku serwera server_sse.js

    Plik server_sse.js ( [listing dokumentu] link do dokumentu )

    // Require needed modules and initialize Express app
    const express = require('express');
    const bodyParser = require('body-parser');
    const cors = require('cors');
    
    const app = express();
    // Middleware for GET /events endpoint
    function eventsHandler(req, res, next) {
      // Mandatory headers and http status to keep connection open
      const headers = {
        'Content-Type': 'text/event-stream',
        'Connection': 'keep-alive',
        'Cache-Control': 'no-cache'
      };
      res.writeHead(200, headers);
      // After client opens connection send all nests as string
      const data = `data: ${JSON.stringify(nests)}\n\n;`
      res.write(data);
      // Generate an id based on timestamp and save res
      // object of client connection on clients list
      // Later we'll iterate it and send updates to each client
      const clientId = Date.now();
      const newClient = {
        id: clientId,
        res
      };
      clients.push(newClient);
      // When client closes connection we update the clients list
      // avoiding the disconnected one
      req.on('close', () => {
        console.log(`${clientId} Connection closed`);
        clients = clients.filter(c => c.id !== clientId);
      });
    }
    // Iterate clients list and use write res object method to send new nest
    function sendEventsToAll(newNest) {
      clients.forEach(c => c.res.write(`data: ${JSON.stringify(newNest)}\n\n`))
    }
    // Middleware for POST /nest endpoint
    async function addNest(req, res, next) {
      const newNest = req.body;
      nests.push(newNest);
      // Send recently added nest as POST result
      res.json(newNest)
      // Invoke iterate and send function
      return sendEventsToAll(newNest);
    }
    // Set cors and bodyParser middlewares
    app.use(cors());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({extended: false}));
    // Define endpoints
    app.post('/nest', addNest);
    app.get('/events', eventsHandler);
    app.get('/status', (req, res) => res.json({clients: clients.length}));
    const PORT = 3001;
    let clients = [];
    let nests = [];
    app.listen(3000);  

H. Token JWT

Tworzymy dwa pliki auth.js.js (serwer uwierzytalniający) i books.js (server udostępniający zasoby). Do realizacji zadania wymagana jest instalacja lokalnie pakietu jsonwebtoken.

 npm i --save jsonwebtoken
  1. Zawartość pliku serwera auth.js

    Plik auth.js ( [listing dokumentu] link do dokumentu )

                                                                                                                                                                                      const express = require('express');
    const bodyParser = require('body-parser');
    const jwt = require('jsonwebtoken');
    
    const app = express();
    
    const accessTokenSecret = 'somerandomaccesstoken';
    const refreshTokenSecret = 'somerandomstringforrefreshtoken';
    
    const users = [
        {
            username: 'john',
            password: 'password123admin',
            role: 'admin'
        }, {
            username: 'anna',
            password: 'password123member',
            role: 'member'
        }
    ]
    
    const refreshTokens = [];
    
    app.use(bodyParser.json());
    
    app.post('/login', (req, res) => {
        // read username and password from request body
        const { username, password } = req.body;
    
        // filter user from the users array by username and password
        const user = users.find(u => { return u.username === username && u.password === password });
        console.log(`${user.username} \n`);
        if (user) {
            // generate an access token
            const accessToken = jwt.sign({ username: user.username, role: user.role }, accessTokenSecret, { expiresIn: '20m' });
            const refreshToken = jwt.sign({ username: user.username, role: user.role }, refreshTokenSecret);
    
            refreshTokens.push(refreshToken);
    
            res.json({
                accessToken,
                refreshToken
            });
        } else {
            res.send('Username or password incorrect');
        }
    });
    
    app.post('/token', (req, res) => {
        const { token } = req.body;
    
        if (!token) {
            return res.sendStatus(401);
        }
    
        if (!refreshTokens.includes(token)) {
            return res.sendStatus(403);
        }
    
        jwt.verify(token, refreshTokenSecret, (err, user) => {
            if (err) {
                return res.sendStatus(403);
            }
    
            const accessToken = jwt.sign({ username: user.username, role: user.role }, accessTokenSecret, { expiresIn: '20m' });
    
            res.json({
                accessToken
            });
        });
    });
    
    app.post('/logout', (req, res) => {
        const { token } = req.body;
        refreshTokens = refreshTokens.filter(token => t !== token);
    
        res.send("Logout successful");
    });
    
    app.listen(3000, () => {
        console.log('Authentication service started on port 3000');
    });  
  2. Tworzymy plik serwera books books.js

    Plik books.js ( [listing dokumentu] link do dokumentu )

    const express = require('express');
    const bodyParser = require('body-parser');
    
    const jwt = require('jsonwebtoken');
    const app = express();
    
    const accessTokenSecret = 'somerandomaccesstoken';
    
    app.use(bodyParser.json());
    
    const authenticateJWT = (req, res, next) => {
        const authHeader = req.headers.authorization;
    
        if (authHeader) {
            const token = authHeader.split(' ')[1];
            //const token = authHeader;
            //console.log(`${authHeader} \n`);
            //console.log(`${token} \n`);
            //console.log(`${accessTokenSecret} \n`);
            //console.log(jwt.verify(token, accessTokenSecret));
            jwt.verify(token, accessTokenSecret, (err, user) => {
                if (err) {
                    console.log('Bledny token');
                    return res.sendStatus(403);
                }
                console.log('Poprawny token');
                req.user = user;
                next();
            });
        } else {
            res.sendStatus(401);
        }
    }
    
    const books = [
        {
            "author": "Chinua Achebe",
            "country": "Nigeria",
            "language": "English",
            "pages": 209,
            "title": "Things Fall Apart",
            "year": 1958
        },
        {
            "author": "Hans Christian Andersen",
            "country": "Denmark",
            "language": "Danish",
            "pages": 784,
            "title": "Fairy tales",
            "year": 1836
        },
        {
            "author": "Dante Alighieri",
            "country": "Italy",
            "language": "Italian",
            "pages": 928,
            "title": "The Divine Comedy",
            "year": 1315
        },
    ]
    
    app.get('/books', authenticateJWT, (req, res) => {
        res.json(books);
    });
    
    app.post('/books', authenticateJWT, (req, res) => {
        const { role } = req.user;
    
        if (role !== 'admin') {
            return res.sendStatus(403);
        }
    
    
        const book = req.body;
        books.push(book);
    
        res.send('book added successfully');
    });
    
    app.listen(4000, () => {
        console.log('Books service started on port 4000');
    });