require('http').Server


const http = require('http');
const server = new http.Server();
    

server.on('request', (req, res) => {
    res.setHeader('content-type', 'text/html');

    res.write('Hello, User!');

    res.end();
});
    

server.listen(8080);
    

require('http').Server

На несуществующие страницы отвечать с кодом 404


server.on('request', (req, res) => {
    if (req.url === '/') {
        res.setHeader('Content-Type', 'text/html');
        res.write('Hello, User!');
    } else {
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.write('Not Found');
    }

    res.end();
});
    

require('http').Server


    

Приветствовать пользователя по имени /name/sergey


server.on('request', (req, res) => {
    const matches = req.url.match(/^\/name\/([a-z]+)/);
    const name = matches && matches[1];

    if (name) {
        res.setHeader('Content-Type', 'text/html');
        res.end(`Hello, ${name}`);
    } else {
        res.statusCode = 404;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Not Found');
    }
});
    

Express.js

Гоголев Сергей

Express.js

$ npm install --save express
    

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

app.get('/name/:name', (req, res) => {
  res.send(`Hello, ${req.params.name}`);
});

app.get('*', (req, res) => {
  res.sendStatus(404);
});

app.listen(8080);
    

Наиболее популярное решение

Поддержка strongloop

loopback

Сервис «Заметки»

Храним заметки пользователя

 GET /               Главная
    
 GET /notes          Список заметок
    
POST /notes          Добавление заметки
    
 GET /notes/films    Просмотр заметки
    

Маршрутизация запросов

 GET /
 GET /notes
POST /notes

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

app.get('/', () => {});
    

app.get('/notes', () => {});
    

app.post('/notes', () => {});
    
 GET /notes
POST /notes

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

app
    .route('/notes')
    .get(() => {})
    .post(() => {})
    
GET /notes/films
GET /notes/books

app.get('/notes/:name', (req, res) => {
    const name = req.params.name;

    res.send(`Note with "${name}" name`);
});
    
?

app.get('/notes?/:name', () => {});
    
GET /notes/films
GET /note/films
+

app.get('/notes+/:name', () => {});
    
GET /notes/films
GET /notesss/films
[]

app.get('/notes/[a-z]+', () => {});
    
GET /notes/films
GET /notes/books
GET /notes/my-books
()
app.get('/notes/:name([a-z]+)', req => {
    console.log(req.params.name)
});

404


app.get('*', (req, res) => res.sendStatus(404));
    

app.post('*', (req, res) => res.sendStatus(404));
    

app.all('*', (req, res) => res.sendStatus(404));
    
app/
└── index.js
└── routes.js
    

module.exports = (app) => {
    app.get('/', (req, res) => {});
};
    

app/
└── index.js
└── routes.js
    

require('./routes')(app);
    
express-route-tester

Разделение ответственности

Паттерн MVC

app/
└── index.js
└── routes.js
└── models
    └── note.js

class Note {
    constructor(props) {
        this.name = props.name;
    }

    save() {}

    static findByName(name) {}

    static findAll {}
}

module.exports = Note;
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    └── notes.js

const Note = require('../models/note');

exports.item = (req, res) => {
    const note = Note.find(req.params.name);

    res.send(note);
}
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    └── notes.js

const Note = require('../models/note');

// ...

exports.list = (req, res) => {
    const notes = Note.findAll();

    res.send('notes', notes);
};
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    └── notes.js

const Note = require('../models/note');

// ...

exports.create = (req, res) => {
    const note = new Note({ name: req.body.name });

    note.save();

    res.sendStatus(201);
};
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    └── notes.js
    └── pages.js

exports.index = (req, res) => {
    res.send('Hello, User!');
}

exports.error404 = (req, res) => {
    res.sendStatus(404);
}
    
app/
└── index.js
└── routes.js
└── models
└── controllers

const pages = require('./controllers/pages');
const notes = require('./controllers/notes');

module.exports = function(app) {
    app.get('/', pages.index);
    app.get('/notes', notes.list);
    app.get('/notes/:name', notes.item);
    app.post('/notes', notes.create);
    app.all('*', pages.error404)
};
    

Шаблонизация

HTML = шаблон + данные

HTML


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>My notes</title>
</head>
<body>
    Hello, User!
</body>
</html>
    

Данные


{
    title: 'My notes',
    message: 'Hello, User!'
}
    

Шаблон


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
</head>
<body>
    {{ message }}
</body>
</html>
    

1. Компиляция

function index(data) {
    return `<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>${data.title}</title>
    </head>
    <body>${data.message}</body>
    </html>`;
}

2. Исполнение

index({
    title: 'My notes',
    message: 'Hello, User!'
})

Handlebars

$ npm install --save hbs
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    

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

app.set('view engine', 'hbs');
require('./routes')(app);

app.listen(8080);
    
app/
└── index.js
└── routes.js
└── models
└── controllers
└── views
    └── index.hbs
    
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
</head>
<body>{{ message }}</body>
</html>
    
app/
└── index.js
└── routes.js
└── models
└── controllers
    └── index.js
└── views
    └── index.hbs
    
exports.index = (req, res) => {
    res.send('Hello, User!');
    res.render('index', {
        title: 'Awesome notes',
        message: '<h1>Hello, User!</h1>'
    });
}
{{ }}
{{ message }}

&lt;h1&gt;Hello, User!&lt;/h1&gt;
    
{{{ }}}
{{{ message }}}

<h1>Hello, User!</h1>
    
{{ meta.description }}
res.render('index', {
    title: 'Awesome notes',
    meta: {
        description: 'My awesome notes',
        charset: 'utf-8'
    }
});
<head>
    <meta charset="{{ meta.charset }}">
    <meta name="description"
          content="{{ meta.description }}">
    <title>{{ title }}</title>
</head>
{{#each }}

res.render('notes', {
    notes: [
        { name: 'Films' },
        { name: 'Books' },
        { name: 'Todo' }
    ]
});
    
    {{#each notes}}
  • {{ name }}
  • {{/each}}
{{#if }}

{{#if notes.length}}
    {{#each notes}}
  • {{ name }}
  • {{/each}}
{{^}}

Notes not found!

{{/if}}
app/
└── index.js
└── routes.js
└── models
└── controllers
└── views
    └── index.hbs
    └── partials
        └── notes.hbs
    
    
const express = require('express');
const app = express();

app.set('view engine', 'hbs');
hbs.registerPartials(__dirname + '/views/partials');
{{> }}

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>{{ title }}</title>
</head>
<body>
    {{> notes}}
</body>
</html>
    
handlebarsjs.com

Цепочки ответственностей

Паттерн Front controller

Паттерн Chain of responsibility

Паттерн Chain of responsibility

  • Запрос последовательно проходит через обработчики
  • Обработчик сам принимает решение, обрабатывать запрос или пропустить дальше

Middlewares

const express = require('express');
const app = express();
// Logs all requests
app.use((req, res, next) => {
    console.log(`→ ${req.method} ${req.originalUrl}`);

    next();
});
$ node index.js

→ GET /
→ GET /notes
→ GET /notes/films
    

Middlewares

app.use((req, res, next) => {
    console.log(`→ ${req.method} ${req.originalUrl}`);
    next();
});
const got = require('got');

app.use((req, res, next) => {
    got('https://example.com/api/')
        .then((data) {
            res.locals.data = data;
            next();
        });
});

require('morgan')


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

app.use(morgan('dev'));
    
GET /notes 200 18.079 ms - 301
|       |    |      |      |
method  url  code   time   bytes
    

require('body-parser')

POST /notes
Accept: application/json
Host: localhost

{
    "name": "films",
    "text": "Films to watch"
}
    

// Собираем по кусочками
req.on('data', (chunk) => {
    body += chunk;
});
    

// Разбираем JSON
JSON.parse(body);
    

require('body-parser')


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

app.use(bodyParser.json());

app.use(() => {
    console.log(req.body);
})
    
{
    name: 'films',
    text: 'Films to watch'
}
    

express.static

app/
└── index.js
└── routes.js
└── models
└── controllers
└── views
└── public
    └── styles.css
    
GET /styles.css
    
const express = require('express');
const app = express();

app.use(express.static(__dirname + '/public')));

Обработка ошибок


app.use(function (err, req, res, next) {
    console.error(err);
    res.sendStatus(500);
});
    
  • Обработчиков может быть несколько
  • Если в middleware произошла ошибка, все последующие игнорируются вплоть до первого обработчика
  • Обработчик ошибок может не прерывать процесс, а передать управление следующей middleware

app.use(bodyParser.json());
    

app.use(function (err, req, res, next) {
    // Выводим ошибку
    console.error(err);

    // Но продолжаем обрабатывать запрос
    next();
});
    

// Помимо ошибки логируем и запрос
app.use(morgan('dev'));
    

Express generator


$ npm install --save --global express-generator
    
$ express --hbs awesome-notes
    
expressjs.com/starter/generator
Пример сервиса «Заметки»