Express, 1

Лекция 05

http://expressjs.com/

Express

  • быстрый
  • гибкий
  • минималистичный
  • веб-фреймворк
  • для Node.js

Установка


npm install express
        

Hello World: Node


const http = require('http');

const server = http.createServer((req, res) => {
  res.end('Hello world!');
});

server.listen(3000, '127.0.0.1', () => {
  console.log('Running');
});
        

Hello World: Express


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

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, function () {
  console.log('Running');
});
        

Hello World: Run


node index.js
        

Статика


app.use(express.static('public'));
        

app.use(express.static('files'));
        

app.use('/static', express.static('public'));
        

Routing

Маршрутизация

Маршрут


app.METHOD(PATH, HANDLER)
            
  • PATH / путь
  • METHOD / метод http-запроса
  • HANDLER / обработчик
  • PATH + METHOD = endpoint
  • 1+ обработчик

METHOD


app.get('/', (req, res) => {
    res.send('GET');
});

app.post('/', (req, res) => {
    res.send('POST');
});

/*
    get, post, put, head, delete, options, trace,
    copy, lock, mkcol, move, purge, propfind,
    proppatch, unlock, report, mkactivity,
    checkout, merge, m-search, notify, subscribe,
    unsubscribe, patch, search, connect
*/
            

METHOD


app['m-search']('/', (req, res) => {
    ...
});
            

METHOD - GET

  • запрос содержимого
  • параметры передаются в URI
  • /path/resource?p1=v1&p2=v2
  • идемпотентный

METHOD - POST

  • передача данных
  • параметры передаются в теле запроса
  • не идемпотентный

METHOD - All


app.all('/', (req, res) => {
  console.log('access to the root');
});
            

PATH

  • строка
  • паттерн
  • регулярное выражение
  • параметры

PATH - Строка


app.get('/', (req, res) => {
  res.send('root');
});
        

app.get('/about', (req, res) => {
  res.send('about');
});
        

PATH - Паттерн


// acd, abcd
app.get('/ab?cd', (req, res) => {
  res.send('ab?cd');
});
        

// abcd, abbcd, abbbcd, ...
app.get('/ab+cd', (req, res) => {
  res.send('ab+cd');
});
        

PATH - Паттерн


//  abcd, abxcd, abRANDOMcd, ab123cd, ...
app.get('/ab*cd', (req, res) => {
  res.send('ab*cd');
});
        

// abe and abcde
app.get('/ab(cd)?e', (req, res) => {
 res.send('ab(cd)?e');
});
        

PATH - RegExp


//  все пути содержащие `a`
app.get(/a/, (req, res) => {
  res.send('/a/');
});
        

// все пути заканчивающиеся на `fly`
app.get(/.*fly$/, (req, res) => {
  res.send('/.*fly$/');
});
        

PATH - Параметры


const route = '/users/:userId/books/:bookId';

app.get(route, (req, res) => {
  res.send(req.params);
});
        

Request URL:
  http://localhost:3000/users/34/books/8989

req.params:
  { "userId": "34", "bookId": "8989" }
        

PATH - Параметры


const route = '/flights/:from-:to';

app.get(route, (req, res) => {
  res.send(req.params);
});
        

Request URL:
  http://localhost:3000/flights/LAX-SFO
req.params:
  { "from": "LAX", "to": "SFO" }
        

PATH

  • паттерн компилируется в регулярное выражение
  • '.' и '-' воспринимаются буквально
  • параметры - [A-Za-z0-9_]

HANDLER


app.get('/', (req, res) => {
  console.log('Hello World!');
});
            

HANDLER - 1+, раздельно


app.get('/', (req, res, next) => {
  console.log('Hello World!');
  next();
});

app.get('/', (req, res) => {
  res.send('Hello World!');
});
            

HANDLER - 1+, слитно


const log = (req, res, next) => {
  console.log('Hello World!');
  next();
};

const send = (req, res) => {
  res.send('Hello World!');
};

app.get('/', log, send);
            

HANDLER - 1+, массив


const log = (req, res, next) => {
  console.log('Hello World!');
  next();
};

const send = (req, res) => {
  res.send('Hello World!');
};

app.get('/', [log, send]);
            

HANDLER - 1+, массив и функции


const log = (req, res, next) => {
  console.log('Hello World!');
  next();
};

const send = (req, res) => {
  res.send('Hello World!');
};

app.get('/', [log, log], send);
            

HANDLER - Цепочка


app.route('/book')
  .get(function(req, res) {
    res.send('Get a random book');
  })
  .post(function(req, res) {
    res.send('Add a book');
  })
  .put(function(req, res) {
    res.send('Update the book');
  });
            

Middleware

Промежуточные обработчики

Middleware


app.get('/', (req, res, next) => {
  console.log('Hello World!');
  next();
});
        

Middleware

Может:

  • выполнять любой код
  • изменять объекты запроса и ответа
  • заканчивать цикл обработки
  • вызывать следующий middleware из очереди

Middleware

Обязан:

  • завершить запрос
  • или вызвать следующий middleware

Middleware - Use


app.use(express.static('public'));
        

const logger = function (req, res, next) {
  console.log(req.url);
  next();
};

app.use(logger);
        

Middleware - Порядок


app.use(logger);
app.get('/', (req, res) => res.send('Hello'));
        

app.get('/', (req, res) => res.send('Hello'));
app.use(logger);
        

Middleware


app.use('/api/*', logger);

app.post('/api/news', parse);
app.post('/api/news', validate);
app.post('/api/news', writeToDb);
app.post('/api/news', respond);
        

Middleware - Типы

  • уровня приложения
  • уровня роутера
  • оброботка ошибок
  • встроенные
  • сторонние

Middleware - Application-level


app.use(function (req, res, next) {
  console.log('Time:', Date.now());
  next();
});
        

Middleware - Router-level


const newsRouter = express.Router();

newsRouter.post('/', parse);
newsRouter.post('/', validate);
newsRouter.post('/', writeToDb);
newsRouter.post('/', respond);

// монтирование роутера
app.use('/api/news', newsRouter);
        

Middleware - Router-level


api.use('/news', newsRouter);
api.use('/user', userRouter);

app.use('/api', api);
        

Middleware - Error-handling


// 4 входных параметра
app.use(function(err, req, res, next) {
  console.error(err.stack);
  res.status(500).send('Something broke!');
});
        

Middleware - Error-handling


// всегда последние
app.use(express.static('public'));
app.use(logger);
app.use('/api', api);
app.use(errorHandler);
        

Middleware - Error-handling


// 1+
app.use(logErrors);
app.use(clientErrorHandler);
app.use(errorHandler);
        

Middleware - Error-handling


const parser = (req, res, next) => {
  req.params.userId
    = parseInt(req.params.userId);

  next();
};

const validator = (req, res, next) => {
  if (isNaN(req.params.userId)) {
    next({
      message: 'User id should be integer'
    });
  }
  else next();
};
        

Middleware - Error-handling


const logError = (err, req, res, next) => {
  console.log(err.message);

  next(err);
};

const errorHandler = (err, req, res, next) => {
  res.send(err.message);
};
        

Middleware - Error-handling


app.use('/news', () => {
  throw new Error('stack overflow!');
});
        

Middleware - Error-handling


app.use('/api/*', (err, req, res, next) => {
  ...
});
        

Middleware - Built-in


const options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  fallthrough: true,
  index: false,
  maxAge: '1h',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
}

app.use(express.static('public', options));
        

Middleware - Static


// dotfiles: 'ignore'

allow
deny - 403
ignore - 404 | default

.gitignore
.idea
        

Middleware - Static


// etag: false

ETag: "686897696a7c876b7e"

If-None-Match: "686897696a7c876b7e"
        

Middleware - Static


// extensions: ['htm', 'html']
        

Middleware - Static


// fallthrough: true

app.use(express.static('public'));
app.use(express.static('files'));
        

Middleware - Static


// index: false
        

Middleware - Static


// maxAge: '1h'

Cache-Control: max-age=3600
        

Middleware - Static


// redirect: false
        

Middleware - Static


/*
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now());
  }
*/
        

Middleware - Third-party


npm install cookie-parser
        

const cookieParser = require('cookie-parser');

// req.cookies
app.use(cookieParser());
        

Middleware - Third-party


npm install body-parser
        

const bodyParser = require('body-parser');

// req.body
app.use(bodyParser.json())