1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
|
---
title: 'Учебник Express часть 4: Маршруты и контроллеры'
slug: Learn/Server-side/Express_Nodejs/routes
translation_of: Learn/Server-side/Express_Nodejs/routes
---
<div>{{LearnSidebar}}</div>
<div>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}</div>
<p class="summary">В этом уроке мы настроим маршруты (код обработки URL) с "фиктивными" функциями-обработчиками для всех конечных точек ресурса, которые нам понадобятся на веб-сайте <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">LocalLibrary</a>. По завершении мы получим модульную структуру для нашего кода обработки маршрута, который будет расширен реальными функциями-обработчиками в следующих статьях. У нас также будет хорошее понимание того, как создавать модульные маршруты с помощью Express!</p>
<table class="learn-box standard-table">
<tbody>
<tr>
<th scope="row">
<table>
<tbody>
<tr>
<th scope="row">Предварительные знания:</th>
</tr>
</tbody>
</table>
</th>
<td>Прочесть <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">введение в Express/Node </a>. Завершить предыдущие уроки (включая <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a>).</td>
</tr>
<tr>
<th scope="row">Цель:</th>
<td>Понять, как создать простые маршруты. Настроить конечные точки URL.</td>
</tr>
</tbody>
</table>
<h2 id="Обзор">Обзор</h2>
<p>В <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">последней статье </a>мы определили модели <em>Mongoose</em> для взаимодействия с базой данных, и использовали (автономный) скрипт, который создал некоторые исходные записи библиотеки. Теперь можно написать код, чтобы представить эту информацию пользователям. Первое, что нужно сделать, это решить, какие возможности для отображения информации мы хотим иметь на наших страницах, а затем определить соответствующие URL-адреса для получения этих ресурсов. Затем нужно будет создать маршруты (обработчики URL-адресов) и представления (шаблоны) для отображения этих страниц.</p>
<p>Приведённая ниже диаграмма напоминает об основном потоке данных и об элементах, которые необходимо реализовать при обработке HTTP-запроса/ответа. Кроме представлений и маршрутов на диаграмме показаны "контроллеры" - функции, которые отделяют код для маршрутизации запросов от кода, который фактически обрабатывает запросы.</p>
<p>Поскольку модели уже созданы, основные элементы, которые следует создать, таковы:</p>
<ul>
<li>"Маршруты" для перенаправления поддерживаемых запросов (и любой закодированной информации в URL-запросах) соответствующим функциям контроллера.</li>
<li>Контроллеры -функции для получения запрашиваемых данных из моделей, создание HTML страницы, отображающей данные, и возращение их пользователю для просмотра в браузере.</li>
<li>Представления (шаблоны), используемые контроллерами для отрисовки данных.</li>
</ul>
<p><img alt="" src="https://mdn.mozillademos.org/files/14456/MVC%20Express.png" style="height: 460px; width: 800px;"></p>
<p>В итоге, у нас должны быть страницы для вывода списков и детальной информации по книгам, жанрам, авторам и экземплярам книг, а также страницы для создания, обновления и удаления записей. Это много для одной статьи. Поэтому большая часть этой статьи будет сосредоточена на настройке наших маршрутов и контроллеров для возврата "фиктивного" контента. Мы расширим методы контроллеров для работы с данными модели в следующих статьях .</p>
<p>В первом разделе ниже приведён краткие основы того, как использовать промежуточное средство (middleware) Express <a href="http://expressjs.com/en/4x/api.html#router">Router</a>. Эти знания будут использованы в следующих разделах при настройке маршрутов для LocalLibrary.</p>
<h2 id="Маршруты_-_основы">Маршруты - основы</h2>
<p>Маршруты - это часть кода Express, связывающая HTTP действия (<code>GET</code>, <code>POST</code>, <code>PUT</code>, <code>DELETE</code>, etc.), URL пути (шаблона), и функцию, которая обрабатывает этот шаблон.</p>
<p>Есть несколько способов создания маршрутов. В этом уроке мы используем промежуточные запросы <code><a href="http://expressjs.com/en/guide/routing.html#express-router">express.Router</a>,</code> так как они позволяют группировать обработчики маршрутов для определённой части сайта и получать к ним доступ через общий префикс маршрута. Все маршруты, связанные с библиотекой, будут сохранены в модуле "catalog", и если мы добавим маршруты для обработки учётных записей пользователей или других функций, мы сможем сгруппировать их отдельно.</p>
<div class="note">
<p><strong>Примечание:</strong> Маршруты приложения Express уже кратко рассматривались в <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Creating_route_handlers">Express Introduction > Creating route handlers</a> (Введение -> Создание обработчиков маршрутов). Применение <em>Router </em>обеспечивает лучшую поддержку модульности (как обсуждается в первой подсекции ниже), а в остальном очень похоже на определение маршрутов непосредственно в объекте приложения <em>Express</em>.</p>
</div>
<p>В оставшейся части этого раздела представлен обзор того, как Router может быть использован для определения маршрутов.</p>
<h3 id="Определение_и_использование_отдельных_модулей_маршрутов">Определение и использование отдельных модулей маршрутов</h3>
<p>Код ниже является реальным примером того, как можно создать модуль маршрута, а затем использовать его в приложении <em>Express</em>.</p>
<p>Первым делом в модуле <strong>wiki.js </strong>создадим маршруты для wiki . Код сначала импортирует объект приложения Express, использует его для получения объекта <code>Router</code> и затем, применяя метод <code>get(),</code> добавляет к объекту пару маршрутов. В завершение модуль экспортирует объект <code>Router</code> .</p>
<pre class="brush: js"><code>// wiki.js - Wiki route module.
var express = require('express');
var router = express.Router();
// Home page route.
router.get('/', function (req, res) {
res.send('Wiki home page');
})
// About page route.
router.get('/about', function (req, res) {
res.send('About this wiki');
})
module.exports = router;</code>
</pre>
<div class="note">
<p><strong>Примечание:</strong> В примере колбэк-функции обработчиков маршрутов определены непосредственно в функциях роутеров. А в LocalLibrary мы определим эти колбэк-функции в отдельном модуле контроллера.</p>
</div>
<p>Чтобы использовать модуль роутера в главном приложении, прежде всего следует выполнить <code>require()</code> модуля маршрута (<strong>wiki.js</strong>). Потом вызовем <code>use()</code> для приложения Express с аргументом, в котором указан URL-путь 'wiki', что добавит Router к пути обработки промежуточного слоя.</p>
<pre class="brush: js"><code>var wiki = require('./wiki.js');
// ...
app.use('/wiki', wiki);</code></pre>
<p>После этого два маршрута, определённые в нашем модуле маршрутов wiki, станут доступны из <code>/wiki/</code> и <code>/wiki/about/</code>.</p>
<h3 id="Функции_Route">Функции Route</h3>
<p>В модуле выше определена пара типовых функций маршрута. Маршрут "about" (ещё раз показан ниже) определён при помощи метода <code>Router.get()</code>, который отвечает только на HTTP GET-запросы. Первый аргумент метода - URL-путь, а второй - колбэк-функция, которая будет вызвана, если получен HTTP GET-запрос с указанным путём.</p>
<pre class="brush: js"><code>router.get('/about', function (req, res) {
res.send('About this wiki');
})</code>
</pre>
<p>Эта колбэк-функция имеет три аргумента takes three arguments (обычно именуемых как указано: <code>req</code>, <code>res</code>, <code>next</code>), которые соответствуют объекту HTTP запроса, ответу HTTP, и <em>следующей</em> <br>
функции в цепочке промежуточных элементов.</p>
<div class="note">
<p><strong>Примечание:</strong> Функции в Router - это промежуточный слой (<a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Using_middleware">middleware</a>) are <a href="https://developer.mozilla.org/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction#Using_middleware">Express </a>, что означает, что они должны или завершить (ответить на) запрос reqили вызвать следующую (<code>next)</code> функцию в цепочке. В нашем случае запрос завершается вызовом <code>send()</code>, поэтому аргумент <code>next</code> не нужен (и поэтому не указан).</p>
<p>Выше у функции роутера только один колбэк-аргумент, но можно указать столько таких аргументов, сколько хотите, или указать массив колбэк-функций. каждая из функций - это элемент в цепочке промежуточного слоя, и они будут вызываться в порядке их добавления в цепочку (если предыдущая функция не завершит запрос).</p>
</div>
<p>Здесь, когда приходит GET-запрос с путём ('<code>/about'</code>) колбэк-функция при ответе вызывает <code><a href="https://expressjs.com/en/4x/api.html#res.send">send()</a></code> , возвращая строку "About this wiki". Существует <a href="https://expressjs.com/en/guide/routing.html#response-methods">ряд других методов ответа</a> , завершающих цикл запрос-ответ. Например, можно вызвать <code><a href="https://expressjs.com/en/4x/api.html#res.json">res.json()</a></code> , чтобы послать ответ JSON, или <code><a href="https://expressjs.com/en/4x/api.html#res.sendFile">res.sendFile()</a>,</code> чтобы послать файл. Метод ответа, который будет использован чаще всего при построении нашей библиотеки - это <a href="https://expressjs.com/en/4x/api.html#res.render">render()</a>, создающий, на основе шаблонов и данных, и возвращающий HTML-файлы —мы поговорим об этом подробнее в следующей статье!</p>
<h3 id="HTTP_глаголы_(действия)">HTTP глаголы (действия)</h3>
<p>Рассмотренный пример использует метод <code>Router.get()</code> для ответа на HTTP GET- запросы с указанным путём.</p>
<p>Кроме того, <code>Router</code> обеспечивает также методы маршрутизации для других HTTP глаголов, которые обычно используются точно таким же способом: <code>post()</code>, <code>put()</code>, <code>delete()</code>, <code>options()</code>, <code>trace()</code>, <code>copy()</code>, <code>lock()</code>, <code>mkcol()</code>, <code>move()</code>, <code>purge()</code>, <code>propfind()</code>, <code>proppatch()</code>, <code>unlock()</code>, <code>report()</code>, <code>mkactivity()</code>, <code>checkout()</code>, <code>merge()</code>, <code>m-</code><code>search()</code>, <code>notify()</code>, <code>subscribe()</code>, <code>unsubscribe()</code>, <code>patch()</code>, <code>search()</code>, и <code>connect()</code>.</p>
<p>Например, код ниже делает то же, что и предыдущий, с путём <code>/about,</code> но отвечает на HTTP POST-запросы.</p>
<pre class="brush: js"><code>router.post('/about', function (req, res) {
res.send('About this wiki');
})</code></pre>
<h3 id="Маршруты_путей">Маршруты путей</h3>
<p>Маршруты путей определяют конечные точки, в которых могут быть сделаны запросы. В уже рассмотренных примерах это были просто строки, которые использовались точно так, как были записаны: '/', '/about', '/book', '/any-random.path'.</p>
<p>Маршруты путей могут быть также образцами строк. Образцы строк используют подмножество синтаксиса регулярных выражений для определения <em>образцов</em> конечных точек, для которых должно проверяться соответствие. Подмножество приведено ниже (отметим, что перенос (<code>-</code>) и точка (<code>.</code>) в путях на основе строк понимаются буквально):</p>
<ul>
<li>? : Конечная точка должна иметь 0 или 1 предыдущий символ. Так, путь маршрута <code>'/ab?cd'</code> соответствует конечным точкам <code>acd</code> или <code>abcd</code> (т.е. b может присутствовать или нет).</li>
<li>+ : Конечная точка должна иметь 1 или более предыдущих символов. Так, путь маршрута <code>'/ab+cd'</code> будет соответствовать конечным точкам <code>abcd</code>, <code>abbcd</code>, <code>abbbcd</code>, и так далее.</li>
<li>* : Конечная точка может содержать произвольную строку там, где находится символ '*'. Так, путь маршрута <code>'ab\*cd'</code> будет соответствовать конечным точкам <code>abcd</code>, <code>abXcd</code>, <code>abSOMErandomTEXTcd</code>, и так далее.</li>
<li>() : Группировка символов для выполнения другой операции. Так, <code>'/ab(cd)?e'</code> выполнит ?-проверку (0 или 1 появление) для группы (cd) — соответствия таковы: <code>abe</code> и <code>abcde</code>.</li>
</ul>
<p>Пути маршрутов могут быть также <a href="/en-US/docs/Web/JavaScript/Guide/Regular_Expressions">регулярными выражениями</a> JavaScript. Например, путь маршрутов (ниже) будет соответствовать <code>catfish и</code> <code>dogfish</code>, но не <code>catflap</code>, <code>catfishhead</code>, и так далее. Отметим, регулярное выражение как путь использует синтаксис регулярных выражений (это не строка в кавычках, как в предыдущих случаях).</p>
<pre class="brush: js"><code>app.get(/.*fish$/, function (req, res) {
...
})</code></pre>
<div class="note">
<p><strong>Примечание:</strong> Большинство наших маршрутов для библиотеки будут просто строками, а не образцами строк или регулярными выражениями. Кроме того, будут использоваться параметры маршрутов, что обсуждается в следующем разделе.</p>
</div>
<h3 id="Параметры_маршрутов">Параметры маршрутов</h3>
<p>Параметры маршрутов - это <em>именованные сегменты URL</em> , которые используются для выбора значений из указанной позиции URL. Именованные сегменты начинаются двоеточием, после которого следует имя (например, <code>/<strong>:</strong>your_parameter_name/</code>. Выбранные значения сохраняются в объекте <code>req.params,</code> причём имя параметра используется как ключ (т.е. <code>req.params.your_parameter_name</code>).</p>
<p>Предположим, например, что URL содержит информацию о пользователях и книгах: <code>http://localhost:3000/users/34/books/8989</code>. Можно извлечь эту информацию (см. ниже) в параметры <code>userId</code> и <code>bookId</code> пути:</p>
<pre><code>app.get('/users/:userId/books/:bookId', function (req, res) {
// доступ к userId через: req.params.userId
// доступ к bookId через: req.params.bookId
res.send(req.params);
})
</code></pre>
<p>Имена параметров пути должны состоять из “символов слова” (A-Z, a-z, 0-9, и _).</p>
<div class="note">
<p><strong>Примечание:</strong> URL <em>/book/create</em> будет соответствовать маршрутам вида <code>/book/:bookId</code> (и '<code>create</code>' станет значением "bookId"). Будет использован первый маршрут, соответствующий введённому URL, поэтому, если необходимо обрабатывать URL вида <code>/book/create</code> отдельно, обработчик этого маршрута должен быть расположен до маршрута <code>/book/:bookId</code> .</p>
</div>
<p>Для начала этих сведений достаточно - если потребуется, можно найти дополнительную информацию в документации Express: <a href="http://expressjs.com/en/starter/basic-routing.html">Basic routing</a> (основы маршрутизации) и <a href="http://expressjs.com/en/guide/routing.html">Routing guide</a> (руководство по маршрутизации). В следующем разделе показано, как задать маршруты и контроллеры для нашей библиотеки LocalLibrary.</p>
<h2 id="Маршруты_необходимые_для_библиотеки_LocalLibrary">Маршруты, необходимые для библиотеки LocalLibrary</h2>
<p>Те URL, которые в итоге будут нужны для наших страниц, показаны ниже. Слово <em>object</em> должно быть заменено на имя каждой из наших моделей (book, bookinstance, genre, author), слово <em>objects</em> - множественное число для <em>object, </em>а <em>id</em> - уникальное значение для поля(<code>_id</code>), которое Mongoose создаёт по умолчанию для каждого экземпляра модели.</p>
<ul>
<li><code>catalog/</code> — Домашняя страница home/index.</li>
<li><code>catalog/<objects>/</code> — Список всех книг, экземпляров книг, жанров и авторов (т.е. /<code>catalog/books/</code>, /<code>catalog/genres/</code>, и т.д.)</li>
<li><code>catalog/<object>/<em><id></em></code> — Страница с подробностями для отдельной книги, экземпляра книги, жанра или автора с заданным полем идентификатора <code><em>_id</em></code> (т.е. <code>/catalog/book/584493c1f4887f06c0e67d37)</code>.</li>
<li><code>catalog/<object>/create</code> — Форма для создания новой книги, экземпляра книги, жанра или автора (т.е. <code>/catalog/book/create)</code>.</li>
<li><code>catalog/<object>/<em><id></em>/update</code> — Форма для обновления отдельной книги, экземпляра книги, жанра или автора с заданным идентификатором <code><em>_id</em></code> (т.е. <code>/catalog/book/584493c1f4887f06c0e67d37/update)</code>.</li>
<li><code>catalog/<object>/<em><id></em>/delete</code> — Форма для удаления отдельной книги, экземпляра книги, жанра или автора по заданному идентификатору <code><em>_id</em></code> (т.е. <code>/catalog/book/584493c1f4887f06c0e67d37/delete)</code>.</li>
</ul>
<p>Первая домашняя страница и страницы со списками не кодируют никакой дополнительной информации. Хотя результаты, возвращаемые запросами, будут зависеть от типа модели и от содержимого БД, запросы для получения этой информации всегда будут одинаковы (подобно тому, как код для создания объектов всегда будет одним и тем же). </p>
<p>В противоположность этому, другие URL используются для работы с определёнными экземплярами документов и моделей — индивидуальность элементов кодируется в URL (как <code><em><id></em></code> выше). Параметры путей используются для извлечения информации и передачи её в обработчик пути (в следующей статье мы применим этот приём для того, чтобы динамически определять, какую информацию следует получить из БД). Кодирование информации в URL-адресе позволяет обойтись одним маршрутом для каждого ресурса определенного типа (например, можно обойтись одним маршрутом для обработки отображения любой отдельной записи о книге).</p>
<div class="note">
<p><strong>Примечание</strong>: Express позволяет строить URL любым способом, который вам нравится — можно кодировать информацию в теле URL как показано выше или использовать URL <code>GET</code> -запрос с параметрами (например, <code>/book/?id=6</code>). Какой бы подход вы не применяли, URL должны быть ясными, логичными и читаемыми (ознакомьтесь с советами<a href="https://www.w3.org/Provider/Style/URI"> W3C</a>).</p>
</div>
<p>Далее мы создадим колбэк-функции обработчиков маршрутов и код маршрутов для всех указанных выше URL.</p>
<h2 id="Создаём_колбэк-функции_обработчиков_маршрутов">Создаём колбэк-функции обработчиков маршрутов</h2>
<p>Перед определением маршрутов сначала создадим фиктивные (каркасные) колбэк-функции, которые они будут вызывать. Эти функции будут храниться в отдельных модулях -"контроллерах" для моделей Book, BookInstance, Genre, и Author (можно использовать любую структуру моделей и файлов, но кажется, что выбранная обеспечивает приемлемую модульность нашего проекта).</p>
<p>Начнём с создания каталога для контроллеров в корне проекта (<strong>/controllers</strong>), а затем создадим отдельные файлы (модули) контроллеров для работы с моделями:</p>
<pre>/express-locallibrary-tutorial //корень проекта
<strong>/controllers</strong>
<strong>authorController.js</strong>
<strong>bookController.js</strong>
<strong>bookinstanceController.js</strong>
<strong>genreController.js</strong></pre>
<h3 id="Контроллер_автора">Контроллер автора</h3>
<p>Скопируем следующий код в файл <strong>/controllers/authorController.js</strong>:</p>
<pre class="brush: js">var Author = require('../models/author');
// Показать список всех авторов.
exports.author_list = function(req, res) {
res.send('NOT IMPLEMENTED: Author list');
};
// Показать подробную страницу для данного автора.
exports.author_detail = function(req, res) {
res.send('NOT IMPLEMENTED: Author detail: ' + req.params.id);
};
// Показать форму создания автора по запросу GET.
exports.author_create_get = function(req, res) {
res.send('NOT IMPLEMENTED: Author create GET');
};
// Создать автора по запросу POST.
exports.author_create_post = function(req, res) {
res.send('NOT IMPLEMENTED: Author create POST');
};
// Показать форму удаления автора по запросу GET.
exports.author_delete_get = function(req, res) {
res.send('NOT IMPLEMENTED: Author delete GET');
};
// Удалить автора по запросу POST.
exports.author_delete_post = function(req, res) {
res.send('NOT IMPLEMENTED: Author delete POST');
};
// Показать форму обновления автора по запросу GET.
exports.author_update_get = function(req, res) {
res.send('NOT IMPLEMENTED: Author update GET');
};
// Обновить автора по запросу POST.
exports.author_update_post = function(req, res) {
res.send('NOT IMPLEMENTED: Author update POST');
};
</pre>
<p>В модуле сначала подключается (requires) модель, которая далее будет использована для получения данных и их обновления. Далее экспортируются функции для каждого URL, который мы хотим обрабатывать (операции create-создать, update-обновить и delete-удалить используют формы, следовательно, должны быть дополнительные методы для обработки post-запросов от форм - эти методы обсуждаются далее, в статье "forms article" ("формы")).</p>
<p>Все функции имеют стандартную форму функций среднего слоя <em>Express </em>, с аргументами для запроса, ответа и следующей <code>(next)</code> функции, которая должна быть вызвана, если метод не завершил цикл запроса (во всех приведённых в коде случаях - завершает!). Методы просто возвращают строку, информирующую о том, что соответствующая страница ещё не создана. Если функция контроллера должна получить параметры маршрута, эти параметры будут выведены в строке сообщения (смотри выше <code>req.params.id</code> ).</p>
<h4 id="BookInstance_controller">BookInstance controller</h4>
<p>Скопируйте следующий код в файл <strong>/controllers/bookinstanceController.js</strong> (он построен по образцу модуля контроллера для автора <code>Author</code> ):</p>
<pre class="brush: js">var BookInstance = require('../models/bookinstance');
// Display list of all BookInstances.
exports.bookinstance_list = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance list');
};
// Display detail page for a specific BookInstance.
exports.bookinstance_detail = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance detail: ' + req.params.id);
};
// Display BookInstance create form on GET.
exports.bookinstance_create_get = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance create GET');
};
// Handle BookInstance create on POST.
exports.bookinstance_create_post = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance create POST');
};
// Display BookInstance delete form on GET.
exports.bookinstance_delete_get = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance delete GET');
};
// Handle BookInstance delete on POST.
exports.bookinstance_delete_post = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance delete POST');
};
// Display BookInstance update form on GET.
exports.bookinstance_update_get = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance update GET');
};
// Handle bookinstance update on POST.
exports.bookinstance_update_post = function(req, res) {
res.send('NOT IMPLEMENTED: BookInstance update POST');
};
</pre>
<h4 id="Контроллер_жанра">Контроллер жанра</h4>
<p>Скопируйте следующий код в файл <strong>/controllers/genreController.js</strong> (он построен по образцу модулей контроллеров для автора <code>Author</code> и экземпляра книги <code>BookInstance</code>):</p>
<pre class="brush: js">var Genre = require('../models/genre');
// Display list of all Genre.
exports.genre_list = function(req, res) {
res.send('NOT IMPLEMENTED: Genre list');
};
// Display detail page for a specific Genre.
exports.genre_detail = function(req, res) {
res.send('NOT IMPLEMENTED: Genre detail: ' + req.params.id);
};
// Display Genre create form on GET.
exports.genre_create_get = function(req, res) {
res.send('NOT IMPLEMENTED: Genre create GET');
};
// Handle Genre create on POST.
exports.genre_create_post = function(req, res) {
res.send('NOT IMPLEMENTED: Genre create POST');
};
// Display Genre delete form on GET.
exports.genre_delete_get = function(req, res) {
res.send('NOT IMPLEMENTED: Genre delete GET');
};
// Handle Genre delete on POST.
exports.genre_delete_post = function(req, res) {
res.send('NOT IMPLEMENTED: Genre delete POST');
};
// Display Genre update form on GET.
exports.genre_update_get = function(req, res) {
res.send('NOT IMPLEMENTED: Genre update GET');
};
// Handle Genre update on POST.
exports.genre_update_post = function(req, res) {
res.send('NOT IMPLEMENTED: Genre update POST');
};
</pre>
<h4 id="Контроллер_книги">Контроллер книги</h4>
<p>Скопируйте следующий код в файл <strong>/controllers/bookController.js</strong>. Он построен по образцу других модулей контроллеров, но ещё содержит функцию <code>index()</code> для вывода странички с приветствием:</p>
<pre class="brush: js">var Book = require('../models/book');
<strong>exports.index = function(req, res) {
res.send('NOT IMPLEMENTED: Site Home Page');
};</strong>
// Display list of all books.
exports.book_list = function(req, res) {
res.send('NOT IMPLEMENTED: Book list');
};
// Display detail page for a specific book.
exports.book_detail = function(req, res) {
res.send('NOT IMPLEMENTED: Book detail: ' + req.params.id);
};
// Display book create form on GET.
exports.book_create_get = function(req, res) {
res.send('NOT IMPLEMENTED: Book create GET');
};
// Handle book create on POST.
exports.book_create_post = function(req, res) {
res.send('NOT IMPLEMENTED: Book create POST');
};
// Display book delete form on GET.
exports.book_delete_get = function(req, res) {
res.send('NOT IMPLEMENTED: Book delete GET');
};
// Handle book delete on POST.
exports.book_delete_post = function(req, res) {
res.send('NOT IMPLEMENTED: Book delete POST');
};
// Display book update form on GET.
exports.book_update_get = function(req, res) {
res.send('NOT IMPLEMENTED: Book update GET');
};
// Handle book update on POST.
exports.book_update_post = function(req, res) {
res.send('NOT IMPLEMENTED: Book update POST');
};
</pre>
<h2 id="Создание_модуля_для_маршрута_catalog">Создание модуля для маршрута catalog</h2>
<p>Далее мы создадим маршруты для всех URL, необходимых веб-сайту<a href="#local_libary_routes"> LocalLibrary</a>, которые будут вызывать функции контроллеров, определённые в предыдущем разделе.</p>
<p>Каркас приложения уже содержит каталог <strong>./routes</strong>, в котором есть маршруты для <em>index</em> и <em>users</em>. Внутри этого каталога создадим ещё один файл маршрутов — <strong>catalog.js</strong> ( см. ниже).</p>
<pre>/express-locallibrary-tutorial //the project root
/routes
index.js
users.js
<strong>catalog.js</strong></pre>
<p>Скопируйте приведённый ниже код в файл <strong>/routes/</strong><strong>catalog.js</strong> :</p>
<pre class="brush: js"><strong>var express = require('express');
var router = express.Router();
</strong>
// Требующиеся модули контроллеров.
var book_controller = require('../controllers/bookController');
var author_controller = require('../controllers/authorController');
var genre_controller = require('../controllers/genreController');
var book_instance_controller = require('../controllers/bookinstanceController');
/// BOOK ROUTES МАРШРУТЫ КНИГ///
// GET catalog home page.
router.get('/', book_controller.index);
// GET request for creating a Book. NOTE This must come before routes that display Book (uses id).
// GET запрос для создания книги. Должен появиться до маршрута, показывающего книгу(использует id)
router.get('/book/create', book_controller.book_create_get);
// POST request for creating Book.
router.post('/book/create', book_controller.book_create_post);
// GET request to delete Book.
router.get('/book/:id/delete', book_controller.book_delete_get);
// POST request to delete Book.
router.post('/book/:id/delete', book_controller.book_delete_post);
// GET request to update Book.
router.get('/book/:id/update', book_controller.book_update_get);
// POST request to update Book.
router.post('/book/:id/update', book_controller.book_update_post);
// GET request for one Book.
router.get('/book/:id', book_controller.book_detail);
// GET request for list of all Book items.
router.get('/books', book_controller.book_list);
/// AUTHOR ROUTES ///
// GET request for creating Author. NOTE This must come before route for id (i.e. display author).
// GET-запрос для создания автора. Должен появиться до маршрута для id (для вывода автора)
router.get('/author/create', author_controller.author_create_get);
// POST request for creating Author.
router.post('/author/create', author_controller.author_create_post);
// GET request to delete Author.
router.get('/author/:id/delete', author_controller.author_delete_get);
// POST request to delete Author.
router.post('/author/:id/delete', author_controller.author_delete_post);
// GET request to update Author.
router.get('/author/:id/update', author_controller.author_update_get);
// POST request to update Author.
router.post('/author/:id/update', author_controller.author_update_post);
// GET request for one Author.
router.get('/author/:id', author_controller.author_detail);
// GET request for list of all Authors.
router.get('/authors', author_controller.author_list);
/// GENRE ROUTES ///
// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
// GET-запрос для создания жанра. Должен появиться до маршрута, выводящего жанр (( с использованием id)
router.get('/genre/create', genre_controller.genre_create_get);
//POST request for creating Genre.
router.post('/genre/create', genre_controller.genre_create_post);
// GET request to delete Genre.
router.get('/genre/:id/delete', genre_controller.genre_delete_get);
// POST request to delete Genre.
router.post('/genre/:id/delete', genre_controller.genre_delete_post);
// GET request to update Genre.
router.get('/genre/:id/update', genre_controller.genre_update_get);
// POST request to update Genre.
router.post('/genre/:id/update', genre_controller.genre_update_post);
// GET request for one Genre.
router.get('/genre/:id', genre_controller.genre_detail);
// GET request for list of all Genre.
router.get('/genres', genre_controller.genre_list);
/// BOOKINSTANCE ROUTES ///
// GET request for creating a BookInstance. NOTE This must come before route that displays BookInstance (uses id).
// GET-запрос для создания экземпляра книги. Должен появиться до маршрута, выводящего BookInstance с использованием id
router.get('/bookinstance/create', book_instance_controller.bookinstance_create_get);
// POST request for creating BookInstance.
router.post('/bookinstance/create', book_instance_controller.bookinstance_create_post);
// GET request to delete BookInstance.
router.get('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_get);
// POST request to delete BookInstance.
router.post('/bookinstance/:id/delete', book_instance_controller.bookinstance_delete_post);
// GET request to update BookInstance.
router.get('/bookinstance/:id/update', book_instance_controller.bookinstance_update_get);
// POST request to update BookInstance.
router.post('/bookinstance/:id/update', book_instance_controller.bookinstance_update_post);
// GET request for one BookInstance.
router.get('/bookinstance/:id', book_instance_controller.bookinstance_detail);
// GET request for list of all BookInstance.
router.get('/bookinstances', book_instance_controller.bookinstance_list);
<strong>module.exports = router;</strong>
</pre>
<p>Модуль загружает Express и использует его для создания объекта <code>Router</code> . В маршрутизаторе задаются маршруты и производится их экспорт.</p>
<p>Маршруты определяют в объекте маршрутизатора или <code>.get()</code> или <code>.post()</code> методы. Все пути заданы как строки (образцы строк и регулярные выражения не использовались). Маршруты, которые взаимодействуют с конкретным ресурсом (скажем, с книгой), для получения из URL идентификатора объекта используют параметры путей.</p>
<p>Все функции-обработчики импортируются из созданных в предыдущем разделе модулей контроллеров.</p>
<h3 id="Обновление_модуля_маршрута_index">Обновление модуля маршрута index</h3>
<p>Все новые маршруты заданы, а маршрут на начальную страницу остался без изменения. Давайте перенаправим его на новую страницу "index", которая создана в каталоге '/catalog'.</p>
<p>Откройте <strong>/routes/index.js</strong> и замените существующий маршрут приведённую ниже.</p>
<pre class="brush: js">// GET home page.
router.get('/', function(req, res) {
res.redirect('/catalog');
});</pre>
<div class="note">
<p><strong>Примечание:</strong> Это первое использование метода ответа <a href="https://expressjs.com/en/4x/api.html#res.redirect">redirect()</a> . Он делает перенаправление на указанную страницу, и по умолчанию устанавливает код возврата HTTP в "302 Found" (найдено). Если требуется, можно изменить код возврата. Путь можно задавать как абсолютный или как относительный.</p>
</div>
<h3 id="Обновление_app.js">Обновление app.js</h3>
<p>Завершающий шаг - добавление маршрутов в цепочку промежуточного слоя. Это будет сделано в <code>app.js</code>.</p>
<p>Откройте файл <strong>app.js</strong> и поместите require для маршрута каталог ниже других маршрутов (добавьте третью строку. показанную ниже, после имеющихся двух строк):</p>
<pre class="brush: js">var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
<strong>var catalogRouter = require('./routes/catalog'); //Import routes for "catalog" area of site</strong></pre>
<p>Далее, добавьте маршрут каталога в стек промежуточного слоя после других маршрутов (добавить третью строку после имеющихся двух):</p>
<pre class="brush: js">app.use('/', indexRouter);
app.use('/users', usersRouter);
<strong>app.use('/catalog', catalogRouter); // Add catalog routes to middleware chain.</strong></pre>
<div class="note">
<p><strong>Примечание:</strong> Мы добавили модуль каталога в путь<code>'/catalog'</code>. Этот путь будет предшествовать всем путям, определённым в модуле каталога. Например, для доступа к списку книг URL будет таким: <code>/catalog/books/</code>.</p>
</div>
<p>Вот так. Теперь у нас есть пути и фиктивные функции, подготовленные для всех URL, которые мы собираемся поддерживать на веб-сайте LocalLibrary.</p>
<h3 id="Проверка_маршрутов">Проверка маршрутов</h3>
<p>Чтобы проверить маршруты, сначала запустим веб-сайт обычным способом</p>
<ul>
<li>Обычный способ
<pre class="brush: bash"><code>// Windows
SET DEBUG=express-locallibrary-tutorial:* & npm start
// macOS or Linux
DEBUG=express-locallibrary-tutorial:* npm start</code>
</pre>
</li>
<li>Если предварительно установлен <a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">nodemon</a>, для запуска можно использовать:
<pre><code>// Windows
SET DEBUG=express-locallibrary-tutorial:* & npm <strong>run devstart</strong>
// macOS or Linux
</code> DEBUG=express-locallibrary-tutorial:* npm <strong style="font-family: inherit; font-size: 1rem;">run devstart</strong>
</pre>
</li>
</ul>
<p>После запуска перейдите к совокупности URL нашей LocalLibrary, и проверьте, что не появляется страница ошибки (HTTP 404). Небольшая часть наших URL для удобства приводится ниже:</p>
<ul>
<li><a href="http://localhost:3000/">http://localhost:3000/</a></li>
<li><a href="http://localhost:3000/catalog">http://localhost:3000/catalog</a></li>
<li><a href="http://localhost:3000/catalog/books">http://localhost:3000/catalog/books</a></li>
<li><a href="http://localhost:3000/catalog/bookinstances/">http://localhost:3000/catalog/bookinstances/</a></li>
<li><a href="http://localhost:3000/catalog/authors/">http://localhost:3000/catalog/authors/</a></li>
<li><a href="http://localhost:3000/catalog/genres/">http://localhost:3000/catalog/genres/</a></li>
<li><a href="http://localhost:3000/catalog/book/5846437593935e2f8c2aa226/">http://localhost:3000/catalog/book/5846437593935e2f8c2aa226</a></li>
<li><a href="http://localhost:3000/catalog/book/create">http://localhost:3000/catalog/book/create</a></li>
</ul>
<h2 id="Итог">Итог</h2>
<p>Созданы все маршруты для нашего сайта. Созданы также фиктивные функции контроллеров, которые мы полностью реализуем в последующих статьях. Попутно мы изучили массу базовых сведений о маршрутах Express, и ознакомились с некоторыми подходами по структурированию маршрутов и контроллеров.</p>
<p>В следующей статье мы создадим настоящую страничку приветствия нашего сайта, для чего используем представления (шаблоны) и данные, хранящиеся в наших моделях.</p>
<h2 id="Смотрите_также">Смотрите также</h2>
<ul>
<li><a href="http://expressjs.com/en/starter/basic-routing.html">Basic routing</a> Основы маршрутизации (документация Express)</li>
<li><a href="http://expressjs.com/en/guide/routing.html">Routing guide</a> Руководство по маршрутизации (документация Express)</li>
</ul>
<p>{{PreviousMenuNext("Learn/Server-side/Express_Nodejs/mongoose", "Learn/Server-side/Express_Nodejs/Displaying_data", "Learn/Server-side/Express_Nodejs")}}</p>
<h2 id="In_this_module">In this module</h2>
<ul>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Introduction">Express/Node introduction</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/development_environment">Setting up a Node (Express) development environment</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Tutorial_local_library_website">Express Tutorial: The Local Library website</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/skeleton_website">Express Tutorial Part 2: Creating a skeleton website</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/mongoose">Express Tutorial Part 3: Using a Database (with Mongoose)</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/routes">Express Tutorial Part 4: Routes and controllers</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/Displaying_data">Express Tutorial Part 5: Displaying library data</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/forms">Express Tutorial Part 6: Working with forms</a></li>
<li><a href="/en-US/docs/Learn/Server-side/Express_Nodejs/deployment">Express Tutorial Part 7: Deploying to production</a></li>
</ul>
|