Node JS

part 2

NPM

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "dependencies": {
    "lodash": "^4.17.21"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "MIT"
}

version http://semver.org:

x.x.x

x.x.1 - path - фикс багов

x.1.x - minor - изменения добавляют новые возможности, но не ломает предыдущие

1.x.x - major - вносят правки которые требуют изменений от потребителей

NPM

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "dependencies": {
    "lodash": "^4.17.21"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node server.js"
  },
  "author": "",
  "license": "MIT"
}

main - задает точку входа в пакет.

Module util

const util = require('util');

const str = util.format("My %s %d %j", "string", 123, {test: "obj"});

console.log(str);
console.log("My %s %d %j", "string", 123, {test: "obj"});
  • %s - String.
  • %d - Number (both integer and float).
  • %j - JSON.
  • %% - single percent sign ('%'). This does not consume an argument.

Наследование от ошибок

const phrase = {
    hello: 'привет',
    world: 'мир',
}
function getPhrase(key) {
    if (!phrase[key]) {
        throw new Error("Phrase absent"); // HTTP 500
    }

    return phrase[key];
}

function makePage(url) {
    if (url !== 'index.html') {
        throw new Error("Page absent"); // HTTP 404
    }

    return url;
}

console.log(makePage('index'))
console.log(getPhrase('index'))

Наследование от ошибок

class PhraseError extends Error {
    constructor(message) {
        super(message);
    }
}
class HttpError extends Error {
    constructor(status, message) {
        super(message);
        this.status = status;
    }

}
const phrase = {
    hello: 'привет',
    world: 'мир',
}
function getPhrase(key) {
    if (!phrase[key]) {
        throw new PhraseError(
          `Phrase absent ${key}`
        ); // HTTP 500
    }
    return phrase[key];
}

function makePage(url) {
    if (url !== 'index.html') {
        throw new HttpError(
          404, "Page absent"
        ); // HTTP 404
    }
    return url;
}
try {
    console.log(makePage('index'))
    console.log(getPhrase('index'))
} catch (err) {
    if (err instanceof HttpError) {
        console.log(err.status, err.message);
    } else {
        console.log( err.message);
    }
}

События EventEmiter

const EventEmitter = require('events').EventEmitter;

const emitter = new EventEmitter();

emitter.on('request', function(request) {
    request.approved = true;
});

emitter.on('request', function(request) {
    console.log(request);
});

emitter.emit('request', {from: 'user 1'});

emitter.emit('request', {from: 'user 2'});

События EventEmiter

server.emit('error')

server.emit('error', new Error('test'))

События EventEmiter

const EventEmitter = require('events').EventEmitter;

const db = new EventEmitter();

class RequestApi {
    constructor() {
        db.on('data', function () {
            this.send({test: 'test'});
        })
    }

    send(data) {
        console.log(data);
    }
}

const req1 = new RequestApi();
const req2 = new RequestApi();

Нужно отписываться от навешенных событий что бы не расходовалась память.

События EventEmiter

const EventEmitter = require('events').EventEmitter;

const db = new EventEmitter();

class RequestApi {
    constructor() {
        db.on('data', this.onData)
    }

    onData(info) {
        this.send(info);
    }

    send(data) {
        console.log(data);
    }

    end() {
        db.removeListener('data', this.onData);
    }
}

const req1 = new RequestApi();
req1.end();
const req2 = new RequestApi();
req2.end();

Реализуем отписку от событий

Web server

const http = require('http');

const server = new http.Server(); // EventEmitter

// Добавляем подписку
server.listen(1337, '127.0.0.1');

// слушаем событие
server.on('request', function (req, res) {
    res.end('Hello');
});

При запуске ноды, нода один раз считывает файл и создает обьект модуля. При изминении в коде нужно перезапускать.

echo server

// http://127.0.0.1:1337/echo?message=Hello -> Hello
const http = require('http');

const server = new http.Server(function (req, res) {
    console.log(req.method, req.url);
    
    res.end(); // если не указать ответ, то браузер будет ждать ответа
});

// Добавляем подписку
server.listen(1337, '127.0.0.1');

echo server

// http://127.0.0.1:1337/echo?message=Hello -> Hello
const http = require('http');
const url = require('url');

const server = new http.Server(function (req, res) {
    console.log(req.method, req.url);
    const urlData = url.parse(req.url, true);
    console.log(urlData);
    res.end(urlData.query.message);
});

// Добавляем подписку
server.listen(1337, '127.0.0.1');

Теперь выведем из req параметров значение

echo server

// http://127.0.0.1:1337/echo?message=Hello -> Hello
const http = require('http');
const url = require('url');

const server = new http.Server(function (req, res) {
    res.statusCode = 404; // status code
    res.end();
});

// Добавляем подписку
server.listen(1337, '127.0.0.1');

Return error.

echo server

// http://127.0.0.1:1337/echo?message=Hello -> Hello
const http = require('http');
const url = require('url');

const server = new http.Server(function (req, res) {
    console.log(req.headers); // headers
    res.end();
});

// Добавляем подписку
server.listen(1337, '127.0.0.1');

headers

http

module fs

const fs = require('fs');
fs.readFile(__filename, (err, data) => {
    if (err) {
        console.error(err)
        return
    }
    console.log(data)
});
const fs = require('fs');
fs.readFile(__filename, (err, data) => {
    if (err) {
        console.error(err)
        return
    }
    console.log(data.toString())
});
const fs = require('fs');
fs.readFile(__filename, 'utf-8', (err, data) => {
...
});

module fs

Что бы проверить наличие файла fs.stat(path[, options], callback)

module fs and path

Что бы проверить наличие файла используем path.

npm i mime

// http://localhost:3000/index.html?secret=true
const http = require('http');
const fs = require('fs');
const url = require('url');
const path = require('path');
const mime = require('mime');

const ROOT = __dirname + "/public/";

http.createServer(function(req, res) {

    if (!checkAccess(req)) {
        res.statusCode = 403;
        res.end("Tell me the secret to access!");
        return;
    }

    sendFileSafe(url.parse(req.url).pathname, res);

}).listen(3000);

function checkAccess(req) {
    return url.parse(req.url, true).query.secret === 'true';
}

function sendFileSafe(filePath, res) {
    try {
        filePath = decodeURIComponent(filePath); // декодируем  %D1%8F
    } catch(e) {
        res.statusCode = 400;
        res.end("Bad Request");
        return;
    }

    // Убираем лишние . и /
    filePath = path.normalize(path.join(ROOT, filePath));

    // Проверям что путь начинается с рута
    if (filePath.indexOf(ROOT) != 0) {
        res.statusCode = 404;
        res.end("File not found");
        return;
    }

    fs.stat(filePath, function(err, stats) {
        if (err || !stats.isFile()) {
            res.statusCode = 404;
            res.end("File not found");
            return;
        }

        sendFile(filePath, res);
    });
}

function sendFile(filePath, res) {
    fs.readFile(filePath, function(err, content) {
        if (err) throw err;

        const mimeType = mime.getType(filePath);
        res.setHeader('Content-Type', mimeType + "; charset=utf-8"); // text/html image/jpeg
        res.end(content);
    });
}

express

Ставим глобально

npm install express-generator -g

что бы использовать генератор.

express --helper

express

Создаем приложение

express

Подключение движка рендера ejs

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
// view engine setup
app.engine('ejs', require('ejs-locals')); // layout partial block
app.set('views', __dirname + '/template');
app.set('view engine', 'ejs');

или ejs-local

нужно установить npm i ejs-locals

express

Обработчики в express называются миделварами.

app.use(function(req, res, next) {
  res.end('');
});

next случжит для того что бы передать управление дальше.

app.use(function(req, res, next) {
  if (req.url == '/test') {
    res.end("Test");
  } else {
    next();
  }
});

app.use(function(req, res, next) {
  if (req.url == '/') {
    res.end("Hello");
  } else {
    next();
  }
});

express

Свой обработчик ошибок

app.use(function(req, res, next) {
  if (req.url == '/forbidden') {
    next(new Error("wops, denied"));
  } else {
    next();
  }
});

app.use(function(err, req, res, next) {
  // NODE_ENV = 'production'
  if (app.get('env') == 'development') {
    var errorHandler = express.errorHandler();
    errorHandler(err, req, res, next);
  } else {
    res.send(500);
  }
});

NodeJS part2

By Oleg Rovenskyi

NodeJS part2

NodeJS part2

  • 230