forked from manuelkiessling/nodebeginner.org
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index-ru.html
2818 lines (2593 loc) · 239 KB
/
index-ru.html
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
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Node.js для начинающих » Подробный учебник по Node.js </title>
<meta name="description" content="Подробный учебник по Node.js для начинающих: Научитесь создавать полномасштабные веб-приложения с серверными JavaScript" />
<link rel="icon" href="favicon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="olddefault-ru.css" />
</head>
<body>
<div id="forkmeongithub">
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook"><img src="fork_me_on_github.png" width="149" height="149" alt="Fork me on GitHub" /></a>
</div>
<div id="translations">
<table>
<tr>
<td>
<a href="http://nodebeginner.org/index-jp.html">
<div class="flag">
<img src="jp-flag.png" width="24" height="24" alt="japanese flag"/>
</div>
<div class="text">日本語で読む</div>
</a>
</td>
<td>
<a href="http://nodebeginner.org/index-es.html">
<div class="flag"><img src="es-flag.png" width="24" height="24" alt="spanish flag" /></div>
<div class="text">Lee este tutorial en Español</div>
</a>
</td>
<td>
<a href="http://nodebeginner.org/index-kr.html">
<div class="flag"><img src="kr-flag.png" width="24" height="24" alt="korean flag" /></div>
<div class="text">이 튜토리얼을 한글로 보세요</div>
</a>
</td>
</tr>
<tr>
<td>
<a href="http://nodebeginner.org/index-zh-cn.html">
<div class="flag"><img src="cn-flag.png" width="24" height="24" alt="chinese flag" /></div>
<div class="text">阅读本书中文版</div>
</a>
</td>
<td>
<a href="http://nodebeginner.org/index-zh-tw.html">
<div class="flag"><img src="cn-flag.png" width="24" height="24" alt="chinese flag" /></div>
<div class="text">阅读本书繁体中文版</div>
</a>
</td>
<td>
<a href="http://nodebeginner.org">
<div class="flag"><img src="us-flag.png" width="24" height="24" alt="usa flag" /></div>
<div class="text">Read this tutorial in english</div>
</a>
</td>
</tr>
</table>
</div>
<div id="book">
<h1>Node.js для начинающих</h1>
<div id="author">Автор: <a href="http://twitter.com/manuelkiessling">Manuel Kiessling</a></div>
<div style="color:#888; font-family:'Helvetica Neue', sans-serif; font-size:75%; margin-left:279px; margin-top:5px;"><a href="http://www.nodebeginner.ru/">Перевод</a>: <a href="http://artod.ru/">Artod</a>, правка 2013-03-24, spmbt</div>
<a name="about"></a>
<h2>О проекте</h2>
<p>
Цель данного документа — помочь вам начать разработку приложений на Node.js и научить всему, что необходимо знать о «продвинутом» JavaScript.
Это больше, чем обычный «Hello world»-туториал.
</p>
<a name="status"></a>
<h3>Статус</h3>
<p>
Вы читаете финальную версию этой книги, в обновлениях исправляются только ошибки или отражаются изменения в новых версиях Node.js. Последнее обновление 12 Февраля 2012.
</p>
<p>
Код примеров этой книги тестировался на <u><b>Node.js версии 0.8.8</b></u> <i style="color:#999">(проверено по англ. версии --прим.перев.)</i>.
</p>
<a name="intended-audience"></a>
<h3>Целевая аудитория</h3>
<p>
Вероятно, документ будет полезен читателям с базовыми знаниями, примерно, как у меня: опыт работы хотя бы с одним объектно-ориентированным языком, таким как Ruby, Python, PHP или Java, небольшой опыт в Javascript и полный новичок в Node.js.
</p>
<p>
Документ рассчитан на разработчиков, уже знакомых с другими языками программирования.
Это значит, что здесь не приводится объяснение действительно базовых вещей, таких как типы данных, переменные, управляющие структуры и т. д.
Вы должные разбираться в этих понятиях, чтобы понимать эту книгу.
</p>
<p>
Однако, поскольку функции и объекты в JavaScript отличаются от своих аналогов в других языках, они будут описаны достаточно подробно.
</p>
<a name="structure"></a>
<h3>Структура учебника</h3>
<p>
Прочитав данный документ до конца, вы сможете создать веб-приложение, которое позволит пользователям просматривать веб-страницы и загружать файлы.
</p>
<p>
Это, конечно, не изменит мир, но мы будем стараться и научимся писать не просто куски кода, которых «достаточно», чтобы сделать это возможным, но и создадим простой, полноценный framework для чистого разделения различных аспектов вашего приложения.
Скоро вы увидите, что я имею в виду.
</p>
<p>
Мы начнём с выяснения того, чем JavaScript в Node.js отличается от JavaScript в браузере.
</p>
<p>
Далее, мы остановимся на написании традиционного «Hello world»-приложения, которое является наиболее простым примером «что-то делающего» кода Node.js.
</p>
<p>
Тогда мы обсудим, какое «реальное» приложение мы хотим создать, проанализируем компоненты, которые необходимо реализовать для написания данного приложения, и начнём работать над каждым из них, шаг за шагом.
</p>
<p>
Как и было обещано, по пути мы узнаем о некоторых продвинутых понятиях JavaScript, о том как их использовать и посмотрим, почему разумно использовать эти понятия вместо привычных нам в других языках программирования.
</p>
<p>
Исходный код законченного приложения доступен в
<a href="https://github.com/ManuelKiessling/NodeBeginnerBook/tree/master/code/application">the NodeBeginnerBook Github репозитории</a>.
</p>
<div id="table-of-contents-headline">Содержание</div>
<div id="table-of-contents">
<ul>
<li><a href="#about">О проекте</a>
<ul>
<li><a href="#status">Статус</a></li>
<li><a href="#intended-audience">Целевая аудитория</a></li>
<li><a href="#structure">Структура учебника</a></li>
</ul>
</li>
<li><a href="#javascript-and-nodejs">JavaScript и Node.js</a>
<ul>
<li><a href="#javascript-and-you">JavaScript и Вы</a></li>
<li><a href="#a-word-of-warning">Предупреждение</a></li>
<li><a href="#server-side-javascript">Server-side JavaScript</a></li>
<li><a href="#hello-world">"Hello World"</a></li>
</ul>
</li>
<li><a href="#a-full-blown-web-application-with-nodejs">Полномасштабное веб-приложение с Node.js</a>
<ul>
<li><a href="#the-use-cases">Что должно делать наше приложение</a></li>
<li><a href="#the-application-stack">Задачи</a></li>
</ul>
</li>
<li><a href="#building-the-application-stack">Реализация приложения</a>
<ul>
<li><a href="#a-basic-http-server">Простой HTTP-сервер</a></li>
<li><a href="#analyzing-our-http-server">Анализ нашего HTTP-сервера</a></li>
<li><a href="#passing-functions-around">Передача функций в качестве параметра</a></li>
<li><a href="#how-function-passing-makes-our-http-server-work">Как анонимная функция делает наш HTTP-сервер рабочим</a></li>
<li><a href="#event-driven-callbacks">Событийно-ориентированные обратные вызовы</a></li>
<li><a href="#how-our-server-handles-requests">Как наш сервер обрабатывает запросы</a></li>
<li><a href="#finding-a-place-for-our-server-module">Выбор места для нашего серверного модуля</a>
</li>
<li><a href="#whats-needed-to-route-requests">Что необходимо для «роутера»?</a></li>
<li><a href="#execution-in-the-kongdom-of-verbs">Исполнение королевских постановлений в царстве глаголов</a></li>
<li><a href="#routing-to-real-request-handlers">Роутинг реальных обработчиков запроса</a></li>
<li><a href="#making-the-request-handlers-respond">Создание ответа обработчиков запроса</a>
<ul>
<li><a href="#how-to-not-do-it">Как делать не надо</a></li>
<li><a href="#blocking-and-non-blocking">Блокирование и неблокирование</a></li>
<li><a href="#responding-request-handlers-with-non-blocking-operations">Ответ обработчиков запроса с неблокирующими операциями.</a>
</li>
</ul>
</li>
<li><a href="#serving-something-useful">Сделаем что-нибудь полезное</a>
<ul>
<li><a href="#handling-post-requests">Обработка POST-запросов</a></li>
<li><a href="#handling-file-uploads">Обработка загрузки файлов</a></li>
</ul>
</li>
<li><a href="#conclusion-and-outlook">Выводы и перспективы</a></li>
</ul>
</li>
</ul>
</div>
<a name="javascript-and-nodejs"></a>
<h2>JavaScript и Node.js</h2>
<h3>JavaScript и Вы</h3>
<p>
До того как мы поговорим о технических вещах, позвольте занять некоторое время и поговорить о вас и ваших отношениях с JavaScript.
Эта глава позволит вам понять, имеет ли смысл читать дальше.
</p>
<p>
Скорее всего, как и в моем случае, вы начали свой путь в веб-разработке с написания простых статических HTML-документов.
Вместе с этим, вы познакомились с веселой штукой, называемой JavaScript, но использовали его исключительно в простых случаях, добавляя интерактивности на ваши веб-странички.
</p>
<p>
Что вы хотели узнать — так это действительно полезные вещи; вы хотели знать, как создать сложный сайт.
Для этого вы изучали PHP, Ruby, Java и начинали писать backend-код.
</p>
<p>
Тем не менее, вы постоянно следили за JavaScript, вы видели, что с появлениям JQuery, Prototype и других фреймворков этот язык стал больше, чем просто <em>window.open()</em>.
</p>
<p>
Однако, это всё ещё относилось к frontend-разработке.
Конечно, jQuery — очень мощный инструмент, но всякий раз, когда вы приправляли ваш сайт разными jQuery-«фишками», в лучшем случае, вы были JavaScript-<em>пользователем</em> нежели JavaScript-<em>разработчиком</em>.
</p>
<p>
А потом пришел Node.js. JavaScript на сервере: насколько это хорошо?
</p>
<p>
И вы решили, что пора проверить старый новый JavaScript.
Подождите. Написать Node.js приложение — одно дело, а понять, почему оно должно быть написано таким образом, для этого нужно понимать JavaScript.
И на этот раз — по-настоящему.
</p>
<p>
В этом — как раз и проблема.
JavaScript живёт двумя, может даже тремя разными жизнями: весёлый маленький DHMTL-помощник из середины 90-х годов, более серьезный frontend-инструмент в лице jQuery и наконец серверный (server-side, backend) JavaScript.
По этой причине не так просто найти информацию, которая поможет вам познать правильный JavaScript, пригодный для написания Node.js приложения в манере, дающий ощущение, что вы не просто использовали JavaScript, а действительно разрабатывали на JavaScript.
</p>
<p>
Это — наиболее правильный подход.
Вы — уже опытный разработчик, вы не хотите изучать новые технологии поверхностно, просто валяя дурака.
Вы хотите быть уверенным, что вы подходите к проблеме под правильным углом.
</p>
<p>
Конечно, существует отличная документация по Node.js, но её зачастую недостаточно. Нужно руководство.
</p>
<p>
Моя цель заключается в обеспечении вас руководством.
</p>
<a name="a-word-of-warning"></a>
<h3>Предупреждение</h3>
<p>
Существуют действительно отличные специалисты в области JavaScript. Я не из их числа.
</p>
<p>
Я — действительно, тот парень, о котором написано в предыдущем параграфе.
Я знаю кое-что о разработке backend веб-приложений, но я всё ещё новичок в «реальном» JavaScript и всё ещё новичок в Node.js.
Я узнал некоторые продвинутые аспекты JavaScript совсем недавно.
Я неопытен.
</p>
<p>
Вот почему эта книга не из разряда «от новичка к эксперту», а скорее «от новичка к продвинутому новичку».
</p>
<p>
Если всё удастся, то этот документ станет тем руководством, которое я хотел бы иметь, когда начинал в Node.js.
</p>
<a name="server-side-javascript"></a>
<h3>Server-side JavaScript</h3>
<p>
Первая инкарнация JavaScript жила в теле браузера.
Но это всего лишь контекст.
Он определяет, что вы можете делать с языком, но не говорит о том, что язык сам по себе может сделать.
JavaScript это «полноценный» язык: вы можете использовать его в различных контекстах и достичь всего того, что можете достичь с другими «полноценными» языками.
</p>
<p>
Node.js — действительно, просто другой контекст: он позволяет вам запускать JavaScript-код вне браузера.
</p>
<p>
Чтобы ваш JavaScript код выполнился на <em>вычислительной машине вне браузера</em> (на <strong>backend</strong>), он должен быть интерпретирован и, конечно же, выполнен.
Именно это и делает Node.js. Для этого он использует движок V8 VM от Google — ту же самую среду исполнения для JavaScript, которую использует браузер Google Chrome.
</p>
<p>
Кроме того, Node.js поставляется со множеством полезных модулей, так что вам не придется писать всё с нуля, как, например, вывод строки в консоль.
</p>
<p>
Таким образом, Node.js состоит из 2 вещей: среды исполнения и полезных библиотек.
</p>
<p>
Для того чтобы их использовать, вам необходимо установить Node.js.
Вместо повторения всего процесса установки здесь, я просто приглашу вас посетить <a href="https://github.com/joyent/node/wiki/Installation" title="Building and Installing Node.js">официальную инструкцию по инсталляции</a>.
Пожалуйста, вернитесь обратно после успешной установки.
</p>
<a name="hello-world"></a>
<h3>«Hello world»</h3>
<p>
Хорошо, давайте пойдём сразу с места в карьер и напишем наше первое Node.js-приложение: «Hello world».
</p>
<p>
Откройте ваш любимый редактор и создайте файл под названием <em>helloworld.js</em>.
Мы хотим вывести строку «Hello world» в консоль, для этого пишем следующий код:
</p>
<pre class="prettyprint lang-js"><span class="pln">console</span><span class="pun">.</span><span
class="pln">log</span><span class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span></pre>
<p>
Сохраняем файл и выполняем его посредством Node.js:
</p>
<pre>node helloworld.js</pre>
<p>
Это должно вывести <em>Hello World</em> на наш терминал.
</p>
<p>
Ладно, всё это скучно, правда? Давайте напишем что-нибудь полезное.
</p>
<a name="a-full-blown-web-application-with-nodejs"></a>
<h2>Полномасштабное веб-приложение с Node.js</h2>
<a name="the-use-cases"></a>
<h3>Что должно делать наше приложение</h3>
<p>
Возьмём что-нибудь попроще, но приближенное к реальности:
</p>
<ul>
<li>
Пользователь должен иметь возможность использовать наше приложение с браузером;
</li>
<li>
Пользователь должен видеть страницу приветствия по адресу http://<em>domain</em>/start;
</li>
<li>
Когда запрашивается http://<em>domain</em>/upload, пользователь должен иметь возможность загрузить картинку со своего компьютера и просмотреть её в своем браузере.
</li>
</ul>
<p>
Вполне достаточно. Конечно, вы могли бы достичь этой цели, немного погуглив и поговнокодив. Но это не то, что нам нужно.
</p>
<p>
Кроме того, мы не хотим писать только простой код для достижения цели, каким бы он элегантным и корректным ни был.
Мы будем интенсивно наращивать больше абстракции, чем это необходимо, чтобы понять как создавать более сложные Node.js-приложения.
</p>
<a name="the-application-stack"></a>
<h3>Задачи</h3>
<p>
Давайте проанализируем наше приложение.
Что нужно, чтобы его реализовать:
</p>
<ul>
<li>
У нас — онлайн веб-приложение, поэтому нам нужен <strong>HTTP-сервер</strong>;
</li>
<li>
Нашему серверу необходимо обслуживать различные запросы в зависимости от URL, по которому был сделан запрос. Для этого нам нужен какой-нибудь роутер (маршрутизатор), чтобы иметь возможность направлять запросы определенным обработчикам;
</li>
<li>
Для выполнения запросов, пришедших на сервер и направляемые роутером, нам нужны действующие <strong>обработчики запросов</strong>;
</li>
<li>
Роутер, вероятно, должен иметь дело с разными входящими POST-данными и передавать их обработчикам запросов в удобной форме. Для этого нам нужен какой-нибудь <strong>обработчик входных данных</strong>;
</li>
<li>
Мы хотим не только обрабатывать запросы, но и показывать пользователю контент по запрошенным URL-адресам, поэтому нам нужна некая <strong>логика отображения</strong> для обработчиков запросов, чтобы иметь возможность отправлять контент пользовательскому браузеру;
</li>
<li>
Последнее, но не менее важное — пользователь сможет загружать картинки, поэтому нам нужен какой-нибудь <strong>обработчик загрузки</strong>, который возьмёт на себя заботу о деталях.
</li>
</ul>
<p>
Давайте подумаем о том, как бы мы реализовали это на PHP.
Скорее всего, типичное решение будет на HTTP-сервере Apache с установленным mod_php5.
<br/>
Это относится к первому пункту наших задач, то есть, «принимать HTTP-запросы и отправлять готовые веб-странички пользователю» — вещи, которые PHP сам не делает.
</p>
<p>
С Node.js — немного иначе.
Потому что в Node.js мы не только создаем наше приложение, мы также реализуем полноценный HTTP-сервер.
Действительно, наше веб-приложение и веб-сервер — в сущности, одно и тоже.
</p>
<p>
Может показаться, что это приведет к лишней работе, но сейчас вы увидите, что с Node.js это не так.
</p>
<p>
Давайте просто начнём реализовывать нашу первую задачу — HTTP-сервер.
</p>
<a name="building-the-application-stack"></a>
<h2>Реализация приложения</h2>
<a name="a-basic-http-server"></a>
<h3>Простой HTTP-сервер</h3>
<p>
Когда я подошел к моменту создания своего первого «реального» Node.js-приложения, я задался вопросом, как организовать мой код.
<br/>
Я должен делать всё в одном файле?
Большинство учебных пособий в интернете учат как создавать простой HTTP-сервер в Node.js, сохраняя всю логику в одном месте.
Что, если я хочу быть уверенным, что мой код останется читабельным по мере реализации всё большего функционала.
</p>
<p>
На самом деле, достаточно легко отыскивать проблемные участки вашего кода, который разделён на модули.
</p>
<p>
Это позволяет вам иметь чистый главный файл, который вы исполняете в Node.js и чистые модули, которые могут использоваться главным файлом и друг другом.
</p>
<p>
Так, давайте создадим главный файл, который мы будем использовать для запуска нашего приложения, и файл модуля, в котором будет находиться наш HTTP-сервер.
</p>
<p>
Я думаю, это более-менее традиционно назвать главным файлом <em>index.js</em>.
А код нашего сервера имеет смысл поместить в файл под названием <em>server.js</em>.
</p>
<p>
Давайте начнём с модуля сервера.
Создайте файл <em>server.js</em> в корневой директории вашего проекта и поместите туда следующий код:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="kwd">function</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}).</span><span
class="pln">listen</span><span class="pun">(</span><span class="lit">8888</span><span
class="pun">);</span></pre>
<p>
И всё!
Вы написали работающий HTTP-сервер.
Давайте проверим его, запустив и протестировав.
Во-первых, выполните ваш скрипт в Node.js:
</p>
<pre>node server.js</pre>
<p>
Теперь откройте ваш браузер и перейдите по адресу <a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a>.
Должна вывестись веб-страница со строкой «Hello world».
</p>
<p>
Правда, это довольно интересно?
Как насчёт того, чтобы поговорить о том, что здесь происходит и оставить на потом вопрос о том, как организовать наш проект?
Я обещаю, мы вернемся к нему.
</p>
<a name="analyzing-our-http-server"></a>
<h3>Анализ нашего HTTP-сервера</h3>
<p>
Хорошо, тогда давайте проанализируем, что здесь действительно происходит.
</p>
<p>
Первая строчка подключает http-модуль, который поставляется вместе с Node.js и делает его доступным через переменную <em>http</em>.
</p>
<p>
Далее, мы вызываем одну из функций http-модуля <em>createServer</em>.
Эта функция возвращает объект, имеющий метод <em>listen</em>, принимающий числовое значение порта нашего HTTP-сервера, который необходимо прослушивать.
</p>
<p>
Пожалуйста, проигнорируйте функцию, которая определяется внутри скобок <em>http.createServer</em>.
</p>
<p>
Мы могли бы написать код, который запускает наш сервер, прослушивающий порт 8888, так:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">var</span><span
class="pln"> server </span><span class="pun">=</span><span class="pln"> http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">();</span><span
class="pln"><br>server</span><span class="pun">.</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span></pre>
<p>
Это запустило бы HTTP-сервер прослушивающего порт 8888, который больше ничего не делает (даже не отвечает на входящие запросы).
</p>
<p>
Действительно интересная (и, если вы привыкли к более консервативным языкам как PHP, довольно странная) часть — это определение функции там, где вы бы ожидали увидеть первый параметр для <em>createServer()</em>.
</p>
<p>
Оказывается, эта определяемая функции и есть первый (и только) параметр, который мы передаём в <em>createServer()</em> при вызове.
Потому что в JavaScript функции могут быть переданы как параметр в другую функцию.
</p>
<a name="passing-functions-around"></a>
<h3>Передача функций в качестве параметра</h3>
<p>
Вы можете в качестве примера сделать что-то подобное:
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> say</span><span
class="pun">(</span><span class="pln">word</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">);</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br></span><span class="kwd">function</span><span class="pln"> execute</span><span
class="pun">(</span><span class="pln">someFunction</span><span class="pun">,</span><span class="pln"> value</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> someFunction</span><span
class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>execute</span><span
class="pun">(</span><span class="pln">say</span><span class="pun">,</span><span
class="pln"> </span><span class="str">"Hello"</span><span class="pun">);</span></pre>
<p>
Разберите пример внимательно! Здесь мы передаём функцию <em>say</em> как первый параметр функции <em>execute</em>.
Не значение, которое возвращает функция <em>say</em>, а саму функцию <em>say</em>!
</p>
<p>
Таким образом, <em>say</em> становится локальной переменной <em>someFunction</em> внутри <em>execute</em> и <em>execute</em> может вызвать функцию в этой переменной вот так: <em>someFunction()</em> (то есть, добавив скобки).
</p>
<p>
Конечно же, так как <em>say</em> принимает один параметр (word), <em>execute</em> может передать какое-либо значение в качестве этого параметра, когда вызывает <em>someFunction</em>.
</p>
<p>
Мы можем, что мы и сделали, передать функцию как параметр в другую функцию.
Но мы не обязаны применять этот косвенный способ, когда сначала определяется функция, а потом передаётся как параметр.
Мы можем определить и передать функцию как параметр в другую функцию прямо на месте:
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> execute</span><span
class="pun">(</span><span class="pln">someFunction</span><span class="pun">,</span><span class="pln"> value</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> someFunction</span><span
class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span
class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>execute</span><span
class="pun">(</span><span class="kwd">function</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">){</span><span class="pln"> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span
class="pln">word</span><span class="pun">)</span><span class="pln"> </span><span
class="pun">},</span><span class="pln"> </span><span class="str">"Hello"</span><span
class="pun">);</span></pre>
<p>
Мы определяем функцию, которую хотим передать в <em>execute</em>, прямо там, где у <em>execute</em> должен быть первый параметр.
</p>
<p>
Из-за того, что нам даже не надо давать имя этой функции, её называют <em>анонимная функция</em>.
</p>
<p>
Это первый проблеск, который я называю «продвинутый» JavaScript, но давайте всё по порядку.
А сейчас давайте просто примем то, что в JavaScript мы можем передать функцию как параметр, когда вызываем другую функцию.
Мы можем сделать это путём присвоения нашей функции переменной, которую му передаем, или путём определения функции для передачи на месте.
</p>
<a name="how-function-passing-makes-our-http-server-work"></a>
<h3>Как анонимная функция делает наш HTTP-сервер рабочим</h3>
<p>
С этими знаниями давайте вернемся назад к нашему минималистичному HTTP-серверу:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="kwd">function</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}).</span><span
class="pln">listen</span><span class="pun">(</span><span class="lit">8888</span><span
class="pun">);</span></pre>
<p>
Сейчас должно быть ясно, что мы здесь делаем: передаём в функцию <em>createServer</em> анонимную функцию.
</p>
<p>
Мы можем добиться того же самого через рефакторинг нашего кода:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br> response</span><span
class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
class="lit">200</span><span class="pun">,</span><span class="pln"> </span><span
class="pun">{</span><span class="str">"Content-Type"</span><span class="pun">:</span><span
class="pln"> </span><span class="str">"text/plain"</span><span class="pun">});</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">write</span><span
class="pun">(</span><span class="str">"Hello World"</span><span class="pun">);</span><span
class="pln"><br> response</span><span class="pun">.</span><span class="pln">end</span><span
class="pun">();</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>http</span><span
class="pun">.</span><span class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span></pre>
<p>
Может сейчас самое время спросить: Почему мы это делаем так?
</p>
<a name="event-driven-callbacks"></a>
<h3>Событийно-ориентированные обратные вызовы</h3>
<p>
Ответ на вопрос a) не так легко дать (по крайней мере для меня), и b) кроется в самой природе работы Node.js — это событийно-ориентированность, то, благодаря чему он работает так быстро.
</p>
<p>
Возможно, вы захотите занять немного своего времени и почитать отличный пост Felix Geisendörfer <a href="http://debuggable.com/posts/understanding-node-js:4bd98440-45e4-4a9a-8ef7-0f7ecbdd56cb">Понимание node.js</a>, чтобы прояснить этот момент.
</p>
<p>
Все сводится к тому факту, что Node.js работает событийно-ориентированно.
Ах да, я тоже до конца не понимаю, что это значит.
Но я постараюсь объяснить, почему это так важно для тех, кто хочет писать веб-приложения в Node.js.
</p>
<p>
Когда вызываем метод <em>http.createServer</em>, мы, конечно, не только хотим иметь сервер, слушающий какой-то порт.
Мы также хотим что-нибудь сделать, когда приходит HTTP-запрос на этот сервер.
</p>
<p>
Проблема состоит в асинхронности: запрос происходит в любой момент времени, в то время как у нас только один процесс, в котором запущен наш сервер.
</p>
<p>
Когда пишем PHP-приложения, мы не беспокоимся обо всем этом: всякий раз, когда приходит HTTP-запрос, веб-сервер (обычно Apache) ответвляет новый процесс специально для этого запроса и запускает соответствующий PHP-скрипт с нуля, который выполняется от начала до конца.
</p>
<p>
Когда приходит новый запрос на порт 8888, относительно потоков управления, мы находимся в середине нашей Node.js-программы.
Как это понять, чтоб не помешаться?
</p>
<p>
Это как раз то, где событийно-ориентированный дизайн Node.js/JavaScript на самом деле помогает. Нам надо узнать некоторые новые понятия, чтобы досконально понять всё это.
</p>
<p>
Мы создаем сервер и передаём функцию в созданный им метод.
Всякий раз, когда наш сервер получает запрос, переданная нами функция будет вызываться.
</p>
<p>
Мы не знаем, когда это произойдет, но у нас теперь есть место, где можем обрабатывать входящий запрос.
Это наша переданная функция и не имеет значения, определили ли мы её сначала или передали анонимно.
</p>
<p>
Этот принцип называется <em>обратный вызов</em> или <em>callback</em>.
Мы передаём в некоторый метод функцию и этот метод исполняет её, когда происходит связанное с методом событие.
</p>
<p>
По крайней мере для меня, это заняло некоторое время, чтобы понять.
Просто почитайте блог Felix Geisendörfer снова, если вы всё ещё не уверены.
</p>
<p>
Давайте немного поиграем с этим новым понятием.
Можем ли мы доказать, что наш код продолжает работать после создания сервера, даже если нет HTTP-запроса и callback-функция, переданная нами, не вызывается?
Давайте попробуем:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> onRequest</span><span class="pun">(</span><span class="pln">request</span><span
class="pun">,</span><span class="pln"> response</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request received."</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br></span><span
class="pun">}</span><span class="pln"><br><br>http</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(</span><span class="pln">onRequest</span><span class="pun">).</span><span class="pln">listen</span><span
class="pun">(</span><span class="lit">8888</span><span class="pun">);</span><span class="pln"><br><br>console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span></pre>
<p>
Обратите внимание, что я использую <em>console.log</em> для вывода текста «Request received.», когда срабатывает функция <em>onRequest</em> (наш callback), а текст «Server has started.» — сразу <em>после</em> запуска HTTP-сервера.
</p>
<p>
Когда мы запустим этот код (как обычно, <em>node server.js</em>), он тут же выведет в командной строке «Server has started.».
Всякий раз, когда мы делаем запрос нашему серверу (через переход по адресу <a href="http://localhost:8888/" rel="nofollow">http://localhost:8888/</a> в нашем браузере), в командной строке выводится сообщение «Request received.».
</p>
<p>
Объектно-ориентированный асинхронный серверный JavaScript с callback-ми в действии :-)
</p>
<p>
(Обратите внимание, что наш сервер, возможно, будет выводить «Request received.» в консоль 2 раза при открытии страницы в браузере.
Это происходит из-за того, что большинство браузеров будут пытаться загрузить фавикон по адресу http://localhost:8888/favicon.ico при запросе http://localhost:8888/)
</p>
<a name="how-our-server-handles-requests"></a>
<h3>Как наш сервер обрабатывает запросы</h3>
<p>
Хорошо, давайте быстро проанализируем остальной код сервера внутри тела нашей callback-функции <em>onRequest()</em>.
</p>
<p>
Когда callback запускается и наша функция <em>onRequest()</em> срабатывает, в неё передаются 2 параметра: <em>request</em> и <em>response</em>.
</p>
<p>
Они являются объектами и вы можете использовать их методы для обработки пришедшего HTTP-запроса и ответа на запрос (то есть, просто что-то посылать по проводам обратно в браузер, который запрашивал ваш сервер).
</p>
<p>
И наш код делает именно это: Всякий раз, когда запрос получен, он использует функцию <em>response.writeHead()</em> для отправки HTTP-статуса 200 и Content-Type в заголовке HTTP-ответа, а функцию <em>Response.Write()</em> для отправки текста «Hello World» в теле HTTP-ответа.
</p>
<p>
И последнее, мы вызываем <em>response.end()</em> чтобы завершить наш ответ.
</p>
<p>
На данный момент, мы не заботимся о деталях запроса, поэтому мы не используем объект <em>request</em> полностью.
</p>
<a name="finding-a-place-for-our-server-module"></a>
<h3>Выбор места для нашего серверного модуля</h3>
<p>
Я обещал, что мы вернёмся к организации нашего приложения.
У нас есть код очень простого HTTP-сервера в файле <em>server.js</em> и я упоминал, что общепринято иметь главный файл с названием <em>index.js</em>, который используется для начальной загрузки и запуска нашего приложения, путём использования других модулей приложения (таких как наш модуль HTTP-сервера в <em>server.js</em>).
</p>
<p>
Давайте поговорим о том, как сделать server.js настоящим Node.js-модулем, чтобы его можно было использовать в нашем главном файле <em>index.js</em>.
</p>
<p>
Как вы могли заметить, мы уже использовали модули в нашем коде:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="pun">...</span><span
class="pln"><br><br>http</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(...);</span></pre>
<p>
Где-то внутри Node.js живёт модуль под названием «http» и мы можем использовать его в нашем коде, путём подключения и присвоения его результата локальной переменной.
</p>
<p>
Это делает нашу локальную переменную объектом, содержащим в себе все публичные методы модуля <em>http</em>.
</p>
<p>
Общепринитая практика — использовать имя модуля для имени локальной переменной, но мы свободны в своём выборе делать, как нам нравится:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> foo </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="pun">...</span><span
class="pln"><br><br>foo</span><span class="pun">.</span><span class="pln">createServer</span><span
class="pun">(...);</span></pre>
<p>
Теперь понятно, как использовать внутренние модули Node.js.
А как создать свой собственный модуль и как его использовать?
</p>
<p>
Давайте выясним это, превратив наш скрипт <em>server.js</em> в настоящий модуль.
</p>
<p>
Оказывается, нам не надо менять слишком многое.
Создание модуля означает, что нам нужно <em>экспортировать</em> какую-либо функциональность этого модуля в скрипт, который его вызывает.
</p>
<p>
Сейчас функционал нашего HTTP-сервера надо экспортировать, что довольно просто: скрипты, подключающие наш модуль сервера, просто запускают сервер.
</p>
<p>
Чтобы сделать это возможным, поместим код нашего сервера в функцию под название <em>start</em> и будем экспортировать эту функцию:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br> </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request received."</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br> </span><span class="pun">}</span><span
class="pln"><br><br> http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
Теперь мы можем создать наш основной файл <em>index.js</em>, и запускать наш HTTP-сервер там, хотя код для сервера находится всё ещё в файле <em>server.js</em>.
</p>
<p>
Создаём файл <em>index.js</em> со следующим содержимым:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> server </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"./server"</span><span
class="pun">);</span><span class="pln"><br><br>server</span><span class="pun">.</span><span class="pln">start</span><span
class="pun">();</span></pre>
<p>
Как вы могли видеть, мы можем использовать модуль сервера просто как внешний модуль: вызвав этот файл и определив для него переменную, экспортированные функции становятся доступны нам.
</p>
<p>
Вот и всё. Сейчас мы можем запустить наше приложение через главный скрипт и он будет делать всё то же самое, что и раньше:
</p>
<pre>node index.js</pre>
<p>
Великолепно — сейчас мы можем помещать различные части нашего приложения в разные файлы и связывать их вместе, посредством превращения их в модули.
</p>
<p>
До сих пор мы работали только с первой частью нашего приложения: получение HTTP-запроса.
Но нам надо что-нибудь с ним делать.
В зависимости от URL, запрошенного браузером у нашего сервера, мы должны реагировать по-разному.
</p>
<p>
В очень простом приложении мы могли бы делать это напрямую внутри callback-функции <em>onRequest()</em>.
Но, как я говорил, давайте добавим немного больше абстракции, чтобы сделать наш пример интереснее.
</p>
<p>
Задание соответствия между разными HTTP-запросами и разными частями нашего кода называется «маршрутизация» («routing», роутинг).
Давайте тогда создадим модуль под названием <em>router</em>.
</p>
<a name="whats-needed-to-route-requests"></a>
<h3>Что необходимо для «роутера»?</h3>
<p>
Нам нужно иметь возможность скармливать запрошенный URL и возможные добавочные GET- и POST-параметры нашему роутеру и, с учётом этого, роутер должен определять, какой код выполнять (этот код есть третья составляющая нашего приложения: коллекция обработчиков запросов, делающие необходимую работу по определённому запросу).
</p>
<p>
Итак, нам надо рассматривать HTTP-запрос и извлекать запрошенный URL, а также GET/POST-параметры.
Можно поспорить, должен ли этот код быть частью роутера или сервера (или даже своего собственного модуля), но давайте сейчас пока просто сделаем его частью сервера.
</p>
<p>
Вся необходимая нам информация доступна через объект <em>request</em>, который передается в качестве первого параметра нашей callback-функции <em>onRequest()</em>.
Чтобы интерпретировать эту информацию, нам необходимо добавить кое-какие Node.js-модули, а именно <em>url</em> и <em>querystring</em>.
</p>
<p>
Модуль <em>url</em> поддерживает методы, которые позволяют нам извлекать различные части URL (такие как запрошенный путь (URL path) и строка параметров запроса (query string)), а <em>querystring</em> в свою очередь, используется для парсинга строки параметров запроса (query string):
</p>
<pre> url.parse(string).query
|
url.parse(string).pathname |
| |
| |
------ -------------------
http://localhost:8888/start?foo=bar&hello=world
--- -----
| |
| |
querystring(string)["foo"] |
|
querystring(string)["hello"]
</pre>
<p>
Конечно, мы также можем использовать <em>querystring</em> для парсинга тела POST-запроса, как мы увидим далее.
</p>
<p>
Давайте сейчас добавим в нашу функцию <em>onRequest()</em> логику, необходимую для извлечения пути URL (pathname), запрошенного браузером:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br> </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br> </span><span class="pun">}</span><span
class="pln"><br><br> http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>
Замечательно.
Теперь наше приложение может различать запросы на основе запрошенного пути URL.
Это позволяет нам направлять запросы нашим обработчикам запросов в зависимости от пути URL, используя наш роутер.
Таким образом, мы можем строить наше приложение RESTful-путём, потому что теперь можем реализовать интерфейс, следующий принципам <em>Идентификации ресурсов</em> (смотри статью в википедии <a href="http://ru.wikipedia.org/wiki/REST">REST</a> для справки).
</p>
<p>
В контексте нашего приложения, это означает, что мы сможем обрабатывать запросы с URL <em>/start</em> и <em>/upload</em> разными частями нашего кода.
Скоро мы увидим, как всё соединяется вместе.
</p>
<p>
Теперь самое время написать наш роутер.
Создаём новый файл под названием <em>router.js</em> со следующим содержимым:
</p>
<pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
class="pun">(</span><span class="pln">pathname</span><span class="pun">)</span><span
class="pln"> </span><span class="pun">{</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"About to route a request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">route </span><span class="pun">=</span><span
class="pln"> route</span><span class="pun">;</span></pre>
<p>
Конечно этот код ничего не делает, но сейчас этого достаточно.
Давайте сначала посмотрим, как скрепить этот роутер с нашим сервером до того как поместим больше логики в роутер.
</p>
<p>
Нашему HTTP-серверу необходимо знать о роутере и использовать его.
Мы могли бы жёстко прописать эти зависимости в нашем сервере, но, так как мы знаем только сложные способы из нашего опыта в других языках программирования, мы сделаем слабосвязанную зависимость сервера и роутера через внедрение этих зависимостей (можете почитать <a href="http://martinfowler.com/articles/injection.html">отличный пост Мартина Фаулера по внедрениям зависимости на английском языке</a> или <a href="http://ru.wikipedia.org/wiki/%D0%92%D0%BD%D0%B5%D0%B4%D1%80%D0%B5%D0%BD%D0%B8%D0%B5_%D0%B7%D0%B0%D0%B2%D0%B8%D1%81%D0%B8%D0%BC%D0%BE%D1%81%D1%82%D0%B8">статью в Википедии на русском языке</a> для дополнительной информации).
</p>
<p>
Для начала, расширим нашу серверную функцию <em>start()</em>, чтобы дать нам возможность передавать функцию <em>route()</em> как параметр:
</p>
<pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> http </span><span
class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
class="str">"http"</span><span class="pun">);</span><span class="pln"><br></span><span
class="kwd">var</span><span class="pln"> url </span><span class="pun">=</span><span
class="pln"> require</span><span class="pun">(</span><span class="str">"url"</span><span
class="pun">);</span><span class="pln"><br><br></span><span class="kwd">function</span><span
class="pln"> start</span><span class="pun">(</span><span class="pln">route</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
class="pln"><br> </span><span class="kwd">function</span><span class="pln"> onRequest</span><span
class="pun">(</span><span class="pln">request</span><span class="pun">,</span><span class="pln"> response</span><span
class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br> </span><span
class="kwd">var</span><span class="pln"> pathname </span><span class="pun">=</span><span class="pln"> url</span><span
class="pun">.</span><span class="pln">parse</span><span class="pun">(</span><span
class="pln">request</span><span class="pun">.</span><span class="pln">url</span><span
class="pun">).</span><span class="pln">pathname</span><span class="pun">;</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request for "</span><span
class="pln"> </span><span class="pun">+</span><span class="pln"> pathname </span><span
class="pun">+</span><span class="pln"> </span><span class="str">" received."</span><span
class="pun">);</span><span class="pln"><br><br> route</span><span class="pun">(</span><span
class="pln">pathname</span><span class="pun">);</span><span
class="pln"><br><br> response</span><span class="pun">.</span><span
class="pln">writeHead</span><span class="pun">(</span><span class="lit">200</span><span
class="pun">,</span><span class="pln"> </span><span class="pun">{</span><span
class="str">"Content-Type"</span><span class="pun">:</span><span class="pln"> </span><span class="str">"text/plain"</span><span
class="pun">});</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">write</span><span class="pun">(</span><span class="str">"Hello World"</span><span
class="pun">);</span><span class="pln"><br> response</span><span class="pun">.</span><span
class="pln">end</span><span class="pun">();</span><span class="pln"><br> </span><span class="pun">}</span><span
class="pln"><br><br> http</span><span class="pun">.</span><span
class="pln">createServer</span><span class="pun">(</span><span class="pln">onRequest</span><span
class="pun">).</span><span class="pln">listen</span><span class="pun">(</span><span
class="lit">8888</span><span class="pun">);</span><span class="pln"><br> console</span><span
class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Server has started."</span><span
class="pun">);</span><span class="pln"><br></span><span class="pun">}</span><span class="pln"><br><br>exports</span><span
class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
class="pln"> start</span><span class="pun">;</span></pre>
<p>