Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

file 2688 lines (2304 sloc) 222.509 kb
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 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688
<!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入门 » 一本全面的Node.js教程</title>
        <meta name="description" content="一本适合Node.js初学者的全面教程:教你如何使用服务端JavaScript来构建一个完整的web应用" />

        <link rel="stylesheet" type="text/css" href="default.css" />
        <style>
            #book p {
                text-align: left;
            }
        </style>
        
        <script type="text/javascript">
            // Google Analytics
            var _gaq = _gaq || [];
            _gaq.push(['_setAccount', 'UA-2127388-6']);
            _gaq.push(['_trackPageview']);

            (function() {
                var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
                ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
                var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
            })();

            // Disqus
            var disqus_shortname = 'nodebeginner';
            var disqus_identifier = 'nodebeginner-book-chinese';
            var disqus_url = 'http://www.nodebeginner.org/index-zh-cn.html';
            (function() {
                var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
                dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
                (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
            })();
        </script>
    </head>
    <body>
        <img style="display: none;" src="the_node_beginner_book_cover_medium.png" height="256" width="171" />
        <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="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="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="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="./">
                            <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>
                    <td>
                        <a href="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://www.nodebeginner.ru">
                            <div class="flag"><img src="ru-flag.png" width="24" height="24" alt="russian flag" /></div>
                            <div class="text">Читать этот учебник на русском</div>
                        </a>
                    </td>
                </tr>
            </table>
        </div>

        <div class="buy-the-bundle cn">
            <div class="cover">
                <a href="/buy-chinese/"><img src="the_node_beginner_book_cover_medium_chinese.png" height="120" width="80" /></a>
            </div>
            <div class="description">
                <p>
                    购买“Node入门”中文版电子书
                </p>
                <p>
                    <strong class="price dollarsign">$</strong><strong class="price">0.99</strong>
                </p>
                <p>
                    <a class="buttonlink" href="/buy-chinese/">
                        <div class="button">立即购买</div>
                    </a>
                </p>
            </div>
            <div class="buy">
                <p>
                    本书共42页
                    <br />
                    支持PDF格式,Kindle以及ePub格式
                    <br />
                    直接下载,免费更新
                </p>
            </div>
        </div>

        <div id="book">
        <h1>Node入门</h1>

        <div id="author">作者: <a href="http://twitter.com/manuelkiessling">Manuel Kiessling</a><br />
                         翻译: <a href="http://weibo.com/goddyzhao">goddyzhao</a> &
                               <a href="http://www.otakustay.com">GrayZhang</a> &
                               <a href="http://weibo.com/cmonday">MondayChen</a></div>

        <a name="about"></a>

        <h2>关于</h2>

        <p>
            本书致力于教会你如何用Node.js来开发应用,过程中会传授你所有所需的“高级”JavaScript知识。本书绝不是一本“Hello World”的教程。
        </p>

        <a name="status"></a>

        <h3>状态</h3>

        <p>
            你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本Node.js的改动进行对应的修正时,才会进行更新。
        </p>

        <p>
            本书中的代码案例都在Node.js 0.6.11版本中测试过,可以正确工作。
        </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>
            读完本书之后,你将完成一个完整的web应用,该应用允许用户浏览页面以及上传文件。
        </p>

        <p>
            当然了,应用本身并没有什么了不起的,相比为了实现该功能书写的代码本身,我们更关注的是如何创建一个框架来对我们应用的不同模块进行干净地剥离。 是不是很玄乎?稍后你就明白了。
        </p>

        <p>
            本书先从介绍在Node.js环境中进行JavaScript开发和在浏览器环境中进行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">本书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">服务器端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的web应用</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>

        <a name="javascript-and-you"></a>

        <h3>JavaScript与你</h3>

        <p>
            抛开技术,我们先来聊聊你以及你和JavaScript的关系。本章的主要目的是想让你看看,对你而言是否有必要继续阅读后续章节的内容。
        </p>

        <p>
            如果你和我一样,那么你很早就开始利用HTML进行“开发”,正因如此,你接触到了这个叫JavaScript有趣的东西,而对于JavaScript,你只会基本的操作——为web页面添加交互。
        </p>

        <p>
            而你真正想要的是“干货”,你想要知道如何构建复杂的web站点 —— 于是,你学习了一种诸如PHP、Ruby、Java这样的编程语言,并开始书写“后端”代码。
        </p>

        <p>
            与此同时,你还始终关注着JavaScript,随着通过一些对jQuery,Prototype之类技术的介绍,你慢慢了解到了很多JavaScript中的进阶技能,同时也感受到了JavaScript绝非仅仅是<em>window.open() </em>那么简单。 .
        </p>

        <p>
            不过,这些毕竟都是前端技术,尽管当想要增强页面的时候,使用jQuery总让你觉得很爽,但到最后,你顶多是个JavaScript<em>用户</em>,而非JavaScript<em>开发者</em>
        </p>

        <p>
            然后,出现了Node.js,服务端的JavaScript,这有多酷啊?
        </p>

        <p>
            于是,你觉得是时候该重新拾起既熟悉又陌生的JavaScript了。但是别急,写Node.js应用是一件事情;理解为什么它们要以它们书写的这种方式来书写则意味着——你要懂JavaScript。这次是玩真的了。
        </p>

        <p>
            问题来了: 由于JavaScript真正意义上以两种,甚至可以说是三种形态存在(从中世纪90年代的作为对DHTML进行增强的小玩具,到像jQuery那样严格意义上的前端技术,一直到现在的服务端技术),因此,很难找到一个“正确”的方式来学习JavaScript,使得让你书写Node.js应用的时候感觉自己是在真正开发它而不仅仅是使用它。
        </p>

        <p>
            因为这就是关键: 你本身已经是个有经验的开发者,你不想通过到处寻找各种解决方案(其中可能还有不正确的)来学习新的技术,你要确保自己是通过正确的方式来学习这项技术。
        </p>

        <p>
            当然了,外面不乏很优秀的学习JavaScript的文章。但是,有的时候光靠那些文章是远远不够的。你需要的是指导。
        </p>

        <p>
            本书的目标就是给你提供指导。
        </p>

        <a name="a-word-of-warning"></a>

        <h3>简短申明</h3>

        <p>
            业界有非常优秀的JavaScript程序员。而我并非其中一员。
        </p>

        <p>
            我就是上一节中描述的那个我。我熟悉如何开发后端web应用,但是对“真正”的JavaScript以及Node.js,我都只是新手。我也只是最近学习了一些JavaScript的高级概念,并没有实践经验。
        </p>

        <p>
            因此,本书并不是一本“从入门到精通”的书,更像是一本“从初级入门到高级入门”的书。
        </p>

        <p>
            如果成功的话,那么本书就是我当初开始学习Node.js最希望拥有的教程。
        </p>

        <a name="server-side-javascript"></a>

        <h3>服务端JavaScript</h3>

        <p>
            JavaScript最早是运行在浏览器中,然而浏览器只是提供了一个上下文,它定义了使用JavaScript可以做什么,但并没有“说”太多关于JavaScript语言本身可以做什么。事实上,JavaScript是一门“完整”的语言: 它可以使用在不同的上下文中,其能力与其他同类语言相比有过之而无不及。
        </p>

        <p>
            Node.js事实上就是另外一种上下文,它允许在后端(脱离浏览器环境)运行JavaScript代码。
        </p>

        <p>
            要实现在后台运行JavaScript代码,代码需要先被解释然后正确的执行。Node.js的原理正是如此,它使用了Google的V8虚拟机(Google的Chrome浏览器使用的JavaScript执行环境),来解释和执行JavaScript代码。
        </p>

        <p>
            除此之外,伴随着Node.js的还有许多有用的模块,它们可以简化很多重复的劳作,比如向终端输出字符串。
        </p>

        <p>
            因此,Node.js事实上既是一个运行时环境,同时又是一个库。
        </p>

        <p>
            要使用Node.js,首先需要进行安装。关于如何安装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>文件。我们要做就是向STDOUT输出“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的web应用</h2>
 
        <a name="the-use-cases"></a>
 
        <h3>用例</h3>
 
        <p>我们来把目标设定得简单点,不过也要够实际才行:</p>

        <ul>
        <li>用户可以通过浏览器使用我们的应用。</li>
        <li>当用户请求<em>http://domain/start</em>时,可以看到一个欢迎页面,页面上有一个文件上传的表单。</li>
        <li>用户可以选择一个图片并提交表单,随后文件将被上传到<em>http://domain/upload</em>,该页面完成上传后会把图片显示在页面上。</li>
        </ul>

        <p>差不多了,你现在也可以去Google一下,找点东西乱搞一下来完成功能。但是我们现在先不做这个。</p>

        <p>更进一步地说,在完成这一目标的过程中,我们不仅仅需要基础的代码而不管代码是否优雅。我们还要对此进行抽象,来寻找一种适合构建更为复杂的Node.js应用的方式。</p>

        <h3>应用不同模块分析</h3>

        <p>我们来分解一下这个应用,为了实现上文的用例,我们需要实现哪些部分呢?</p>

        <ul>
        <li>我们需要提供Web页面,因此需要一个<em>HTTP服务器</em></li>
        <li>对于不同的请求,根据请求的URL,我们的服务器需要给予不同的响应,因此我们需要一个<em>路由</em>,用于把请求对应到请求处理程序(request handler)</li>
        <li>当请求被服务器接收并通过路由传递之后,需要可以对其进行处理,因此我们需要最终的<em>请求处理程序</em></li>
        <li>路由还应该能处理POST数据,并且把数据封装成更友好的格式传递给请求处理入程序,因此需要<em>请求数据处理功能</em></li>
        <li>我们不仅仅要处理URL对应的请求,还要把内容显示出来,这意味着我们需要一些<em>视图逻辑</em>供请求处理程序使用,以便将内容发送给用户的浏览器</li>
        <li>最后,用户需要上传图片,所以我们需要<em>上传处理功能</em>来处理这方面的细节</li>
        </ul>

        <p>我们先来想想,使用PHP的话我们会怎么构建这个结构。一般来说我们会用一个Apache HTTP服务器并配上mod_php5模块。<br />从这个角度看,整个“接收HTTP请求并提供Web页面”的需求根本不需要PHP来处理。</p>

        <p>不过对Node.js来说,概念完全不一样了。使用Node.js时,我们不仅仅在实现一个应用,同时还实现了整个HTTP服务器。事实上,我们的Web应用以及对应的Web服务器基本上是一样的。</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应用的时候,我不但不知道怎么写Node.js代码,也不知道怎么组织这些代码。
            <br>
            我应该把所有东西都放进一个文件里吗?网上有很多教程都会教你把所有的逻辑都放进一个用Node.js写的基础HTTP服务器里。但是如果我想加入更多的内容,同时还想保持代码的可读性呢?
        </p>

        <p>

            实际上,只要把不同功能的代码放入不同的模块中,保持代码分离还是相当简单的。
        </p>

        <p>
            这种方法允许你拥有一个干净的主文件(main file),你可以用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>&nbsp; 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>&nbsp; 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>&nbsp; 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>
            这很有趣,不是吗?让我们先来谈谈HTTP服务器的问题,把如何组织项目的事情先放一边吧,你觉得如何?我保证之后我们会解决那个问题的。
        </p>

        <a name="analyzing-our-http-server"></a>

        <h3>分析HTTP服务器</h3>

        <p>
            那么接下来,让我们分析一下这个HTTP服务器的构成。
        </p>

        <p>
            第一行<em>请求(require)</em>Node.js自带的 <em>http</em> 模块,并且把它赋值给 <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>
            这段代码只会启动一个侦听8888端口的服务器,它不做任何别的事情,甚至连请求都不会应答。
        </p>

        <p>
            最有趣(而且,如果你之前习惯使用一个更加保守的语言,比如PHP,它还很奇怪)的部分是 <em>createSever()</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>&nbsp; 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>&nbsp; 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>execute</em> 中的本地变量 <em>someFunction</em> ,execute可以通过调用 <em>someFunction()</em> (带括号的形式)来使用 <em>say</em> 函数。
        </p>

        <p>
            当然,因为 <em>say</em> 有一个变量, <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>&nbsp; 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>&nbsp; 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>&nbsp; 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>&nbsp; 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>&nbsp; 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>&nbsp; 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>&nbsp; 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>这个问题可不好回答(至少对我来说),不过这是Node.js原生的工作方式。它是事件驱动的,这也是它为什么这么快的原因。 </p>

        <p>你也许会想花点时间读一下Felix Geisendörfer的大作<a href="http://debuggable.com/posts/understanding-node-js:4bd98440-45e4-4a9a-8ef7-0f7ecbdd56cb">Understanding node.js</a>,它介绍了一些背景知识。 </p>

        <p>这一切都归结于“Node.js是事件驱动的”这一事实。好吧,其实我也不是特别确切的了解这句话的意思。不过我会试着解释,为什么它对我们用Node.js写网络应用(Web based application)是有意义的。 </p>

        <p>当我们使用 <em>http.createServer</em> 方法的时候,我们当然不只是想要一个侦听某个端口的服务器,我们还想要它在服务器收到一个HTTP请求的时候做点什么。 </p>

        <p>问题是,这是异步的:请求任何时候都可能到达,但是我们的服务器却跑在一个单进程中。 </p>

        <p>写PHP应用的时候,我们一点也不为此担心:任何时候当有请求进入的时候,网页服务器(通常是Apache)就为这一请求新建一个进程,并且开始从头到尾执行相应的PHP脚本。 </p>

        <p>那么在我们的Node.js程序中,当一个新的请求到达8888端口的时候,我们怎么控制流程呢? </p>

        <p>嗯,这就是Node.js/JavaScript的事件驱动设计能够真正帮上忙的地方了——虽然我们还得学一些新概念才能掌握它。让我们来看看这些概念是怎么应用在我们的服务器代码里的。 </p>

        <p>我们创建了服务器,并且向创建它的方法传递了一个函数。无论何时我们的服务器收到一个请求,这个函数就会被调用。 </p>

        <p>我们不知道这件事情什么时候会发生,但是我们现在有了一个处理请求的地方:它就是我们传递过去的那个函数。至于它是被预先定义的函数还是匿名函数,就无关紧要了。 </p>

        <p>这个就是传说中的 <em>回调</em> 。我们给某个方法传递了一个函数,这个方法在有相应事件发生时调用这个函数来进行 <em>回调</em></p>

        <p>至少对我来说,需要一些功夫才能弄懂它。你如果还是不太确定的话就再去读读Felix的博客文章。 </p>

        <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></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>&nbsp; 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>&nbsp; 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>&nbsp; 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>&nbsp; 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>onRequest</em> (我们的回调函数)触发的地方,我用 <em>console.log</em> 输出了一段文本。在HTTP服务器开始工作<em>之后</em>,也输出一段文本。 </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和它的回调啦!</p>

        <p>(请注意,当我们在服务器访问网页时,我们的服务器可能会输出两次“Request received.”。那是因为大部分服务器都会在你访问 http://localhost:8888 /时尝试读取 http://localhost:8888/favicon.ico )</p>

        <a name="how-our-server-handles-requests"></a>

        <h3>服务器是如何处理请求的</h3>

        <p>好的,接下来我们简单分析一下我们服务器代码中剩下的部分,也就是我们的回调函数 <em>onRequest()</em> 的主体部分。 </p>

        <p>当回调启动,我们的 <em>onRequest()</em> 函数被触发的时候,有两个参数被传入: <em>request</em><em>response</em></p>

        <p>它们是对象,你可以使用它们的方法来处理HTTP请求的细节,并且响应请求(比如向发出请求的浏览器发回一些东西)。 </p>

        <p>所以我们的代码就是:当收到请求时,使用 <em>response.writeHead()</em> 函数发送一个HTTP状态200和HTTP头的内容类型(content-type),使用 <em>response.write()</em> 函数在HTTP相应主体中发送文本“Hello World"。 </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>OK,就像我保证过的那样,我们现在可以回到我们如何组织应用这个问题上了。我们现在在 <em>server.js</em> 文件中有一个非常基础的HTTP服务器代码,而且我提到通常我们会有一个叫 <em>index.js</em> 的文件去调用应用的其他模块(比如 <em>server.js</em> 中的HTTP服务器模块)来引导和启动应用。 </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>&nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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>正如你所看到的,我们可以像使用任何其他的内置模块一样使用server模块:请求这个文件并把它指向一个变量,其中已导出的函数就可以被我们使用了。</p>

        <p>好了。我们现在就可以从我们的主要脚本启动我们的的应用了,而它还是老样子:</p>
        <pre>node index.js</pre>

        <p>非常好,我们现在可以把我们的应用的不同部分放入不同的文件里,并且通过生成模块的方式把它们连接到一起了。 </p>

        <p>我们仍然只拥有整个应用的最初部分:我们可以接收HTTP请求。但是我们得做点什么——对于不同的URL请求,服务器应该有不同的反应。 </p>

        <p>对于一个非常简单的应用来说,你可以直接在回调函数 <em>onRequest()</em> 中做这件事情。不过就像我说过的,我们应该加入一些抽象的元素,让我们的例子变得更有趣一点儿。 </p>

        <p>处理不同的HTTP请求在我们的代码中是一个不同的部分,叫做“路由选择”——那么,我们接下来就创造一个叫做 <em>路由</em> 的模块吧。 </p>

        <a name="whats-needed-to-route-requests"></a>

        <h3>如何来进行请求的“路由”</h3>

         <p>我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码(这里“代码”对应整个应用的第三部分:一系列在接收到请求时真正工作的处理程序)。</p>

        <p>因此,我们需要查看HTTP请求,从中提取出请求的URL以及GET/POST参数。这一功能应当属于路由还是服务器(甚至作为一个模块自身的功能)确实值得探讨,但这里暂定其为我们的HTTP服务器的功能。</p>

        <p>我们需要的所有数据都会包含在request对象中,该对象作为<em>onRequest()</em>回调函数的第一个参数传递。但是为了解析这些数据,我们需要额外的Node.JS模块,它们分别是<em>url</em><em>querystring</em>模块。</p>
<pre> url.parse(string).query
                                           |
           url.parse(string).pathname |
                       | |
                       | |
                     ------ -------------------
http://localhost:8888/start?foo=bar&amp;hello=world
                                --- -----
                                 | |
                                 | |
              querystring(string)["foo"] |
                                            |
                         querystring(string)["hello"]
</pre>

        <p>当然我们也可以用<em>querystring</em>模块来解析POST请求体中的参数,稍后会有演示。</p>

        <p>现在我们来给<em>onRequest()</em>函数加上一些逻辑,用来找出浏览器请求的URL路径:</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>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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路径为基准映射到处理程序上。</p>

        <p>在我们所要构建的应用中,这意味着来自<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>&nbsp; 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>我们的服务器应当知道路由的存在并加以有效利用。我们当然可以通过硬编码的方式将这一依赖项绑定到服务器上,但是其它语言的编程经验告诉我们这会是一件非常痛苦的事,因此我们将使用依赖注入的方式较松散地添加路由模块(你可以读读<a href="http://martinfowler.com/articles/injection.html">Martin Fowlers关于依赖注入的大作</a>来作为背景知识)。</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></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>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; route</span><span class="pun">(</span><span
                class="pln">pathname</span><span class="pun">);</span><span
                class="pln"><br><br>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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>,使得路由函数可以被注入到服务器中:</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></span><span class="kwd">var</span><span
                class="pln"> router </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
                class="str">"./router"</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><span
                class="pln">router</span><span class="pun">.</span><span class="pln">route</span><span
                class="pun">);</span><span class="pln"><br></span></pre>

        <p>在这里,我们传递的函数依旧什么也没做。</p>

        <p>如果现在启动应用(<em>node index.js,始终记得这个命令行</em>),随后请求一个URL,你将会看到应用输出相应的信息,这表明我们的HTTP服务器已经在使用路由模块了,并会将请求的路径传递给路由:</p>

        <pre>bash$ node index.js
Request for /foo received.
About to route a request for /foo</pre>

        <p>(以上输出已经去掉了比较烦人的/favicon.ico请求相关的部分)。</p>
 
        <a name="execution-in-the-kongdom-of-verbs"></a>
 
        <h3>行为驱动执行</h3>

        <p>请允许我再次脱离主题,在这里谈一谈函数式编程。</p>

        <p>将函数作为参数传递并不仅仅出于技术上的考量。对软件设计来说,这其实是个哲学问题。想想这样的场景:在index文件中,我们可以将<em>router</em>对象传递进去,服务器随后可以调用这个对象的<em>route</em>函数。</p>

        <p>就像这样,我们传递一个东西,然后服务器利用这个东西来完成一些事。嗨那个叫路由的东西,能帮我把这个路由一下吗?</p>

        <p>但是服务器其实不需要这样的东西。它只需要把事情做完就行,其实为了把事情做完,你根本不需要东西,你需要的是动作。也就是说,你不需要<em>名词</em>,你需要<em>动词</em></p>

        <p>理解了这个概念里最核心、最基本的思想转换后,我自然而然地理解了函数编程。</p>

        <p>我是在读了Steve Yegge的大作<a href="http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html">名词王国中的死刑</a>之后理解函数编程。你也去读一读这本书吧,真的。这是曾给予我阅读的快乐的关于软件的书籍之一。</p>

        <a name="routing-to-real-request-handlers"></a>
 
        <h3>路由给真正的请求处理程序</h3>

         <p>回到正题,现在我们的HTTP服务器和请求路由模块已经如我们的期望,可以相互交流了,就像一对亲密无间的兄弟。</p>

        <p>当然这还远远不够,路由,顾名思义,是指我们要针对不同的URL有不同的处理方式。例如处理<em>/start</em>的“业务逻辑”就应该和处理<em>/upload</em>的不同。</p>

        <p>在现在的实现下,路由过程会在路由模块中“结束”,并且路由模块并不是真正针对请求“采取行动”的模块,否则当我们的应用程序变得更为复杂时,将无法很好地扩展。</p>

        <p>我们暂时把作为路由目标的函数称为请求处理程序。现在我们不要急着来开发路由模块,因为如果请求处理程序没有就绪的话,再怎么完善路由模块也没有多大意义。</p>

        <p>应用程序需要新的部件,因此加入新的模块 -- 已经无需为此感到新奇了。我们来创建一个叫做requestHandlers的模块,并对于每一个请求处理程序,添加一个占位用函数,随后将这些函数作为模块的方法导出:</p>

        <pre class="prettyprint lang-js"><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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</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"> upload</span><span
                class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>这样我们就可以把请求处理程序和路由模块连接起来,让路由“有路可寻”。</p>

        <p>在这里我们得做个决定:是将requestHandlers模块硬编码到路由里来使用,还是再添加一点依赖注入?虽然和其他模式一样,依赖注入不应该仅仅为使用而使用,但在现在这个情况下,使用依赖注入可以让路由和请求处理程序之间的耦合更加松散,也因此能让路由的重用性更高。</p>

        <p>这意味着我们得将请求处理程序从服务器传递到路由中,但感觉上这么做更离谱了,我们得一路把这堆请求处理程序从我们的主文件传递到服务器中,再将之从服务器传递到路由。</p>

        <p>那么我们要怎么传递这些请求处理程序呢?别看现在我们只有2个处理程序,在一个真实的应用中,请求处理程序的数量会不断增加,我们当然不想每次有一个新的URL或请求处理程序时,都要为了在路由里完成请求到处理程序的映射而反复折腾。除此之外,在路由里有一大堆<em>if request == x then call handler y</em>也使得系统丑陋不堪。</p>

        <p>仔细想想,有一大堆东西,每个都要映射到一个字符串(就是请求的URL)上?似乎关联数组(associative array)能完美胜任。</p>

        <p>不过结果有点令人失望,JavaScript没提供关联数组 -- 也可以说它提供了?事实上,在JavaScript中,真正能提供此类功能的是它的对象。</p>

        <p>在这方面,<a href="http://msdn.microsoft.com/en-us/magazine/cc163419.aspx">http://msdn.microsoft.com/en-us/magazine/cc163419.aspx</a>有一个不错的介绍,我在此摘录一段:</p>

         <blockquote>
            <p>在C++或C#中,当我们谈到对象,指的是类或者结构体的实例。对象根据他们实例化的模板(就是所谓的类),会拥有不同的属性和方法。但在JavaScript里对象不是这个概念。在JavaScript中,对象就是一个键/值对的集合 -- 你可以把JavaScript的对象想象成一个键为字符串类型的字典。</p>
        </blockquote>

        <p>但如果JavaScript的对象仅仅是键/值对的集合,它又怎么会拥有方法呢?好吧,这里的值可以是字符串、数字或者……函数!</p>

        <p>好了,最后再回到代码上来。现在我们已经确定将一系列请求处理程序通过一个对象来传递,并且需要使用松耦合的方式将这个对象注入到<em>route()</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></span><span class="kwd">var</span><span
                class="pln"> router </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
                class="str">"./router"</span><span class="pun">);</span><span class="pln"><br></span><span class="kwd">var</span><span
                class="pln"> requestHandlers </span><span class="pun">=</span><span class="pln"> require</span><span
                class="pun">(</span><span class="str">"./requestHandlers"</span><span class="pun">);</span><span
                class="pln"><br><br></span><span class="kwd">var</span><span class="pln"> handle </span><span
                class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span
                class="pln"><br>handle</span><span class="pun">[</span><span class="str">"/"</span><span
                class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
                class="pun">[</span><span class="str">"/start"</span><span class="pun">]</span><span
                class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
                class="pun">[</span><span class="str">"/upload"</span><span class="pun">]</span><span
                class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">upload</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><span
                class="pln">router</span><span class="pun">.</span><span class="pln">route</span><span
                class="pun">,</span><span class="pln"> handle</span><span class="pun">);</span></pre>

        <p>虽然<em>handle</em>并不仅仅是一个“东西”(一些请求处理程序的集合),我还是建议以一个动词作为其命名,这样做可以让我们在路由中使用更流畅的表达式,稍后会有说明。</p>

        <p>正如所见,将不同的URL映射到相同的请求处理程序上是很容易的:只要在对象中添加一个键为<em>"/"</em>的属性,对应<em>requestHandlers.start</em>即可,这样我们就可以干净简洁地配置<em>/start</em><em>/</em>的请求都交由<em>start</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></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"> handle</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; route</span><span class="pun">(</span><span
                class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span class="pun">);</span><span
                class="pln"><br><br>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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>start()</em>函数里添加了<em>handle</em>参数,并且把handle对象作为第一个参数传递给了<em>route()</em>回调函数。</p>

        <p>然后我们相应地在<em>route.js</em>文件中修改<em>route()</em>函数:</p>
        <pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> route</span><span
                class="pun">(</span><span class="pln">handle</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>&nbsp; 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>&nbsp; </span><span class="kwd">if</span><span
                class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
                class="pln"> handle</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"> </span><span class="str">'function'</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; handle</span><span
                class="pun">[</span><span class="pln">pathname</span><span class="pun">]();</span><span class="pln"><br>&nbsp; </span><span
                class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
                class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </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>通过以上代码,我们首先检查给定的路径对应的请求处理程序是否存在,如果存在的话直接调用相应的函数。我们可以用从关联数组中获取元素一样的方式从传递的对象中获取请求处理函数,因此就有了简洁流畅的形如<em>handle&#91;pathname&#93;();</em>的表达式,这个感觉就像在前方中提到的那样:“嗨,请帮我处理了这个路径”。</p>

        <p>有了这些,我们就把服务器、路由和请求处理程序在一起了。现在我们启动应用程序并在浏览器中访问<em>http://localhost:8888/start</em>,以下日志可以说明系统调用了正确的请求处理程序:</p>

        <pre>Server has started.
Request for /start received.
About to route a request for /start
Request handler 'start' was called.</pre>

         <p>并且在浏览器中打开<em>http://localhost:8888/</em>可以看到这个请求同样被<em>start</em>请求处理程序处理了:</p>
        <pre>Request for / received.
About to route a request for /
Request handler 'start' was called.</pre>

        <a name="making-the-request-handlers-respond"></a>

        <h3>让请求处理程序作出响应</h3>

        <p>
            很好。不过现在要是请求处理程序能够向浏览器返回一些有意义的信息而并非全是“Hello World”,那就更好了。
        </p>

        <p>
            这里要记住的是,浏览器发出请求后获得并显示的“Hello World”信息仍是来自于我们<em>server.js</em>文件中的<em>onRequest</em>函数。
        </p>

        <p>
            其实“处理请求”说白了就是“对请求作出响应”,因此,我们需要让请求处理程序能够像<em>onRequest</em>函数那样可以和浏览器进行“对话”。
        </p>

        <a name="how-to-not-do-it"></a>

        <h4>不好的实现方式</h4>

        <p>
            对于我们这样拥有PHP或者Ruby技术背景的开发者来说,最直截了当的实现方式事实上并不是非常靠谱: 看似有效,实则未必如此。
        </p>

        <p>
            这里我指的“直截了当的实现方式”意思是:让请求处理程序通过<em>onRequest</em>函数直接返回(<em>return()</em>)他们要展示给用户的信息。
        </p>

        <p>
            我们先就这样去实现,然后再来看为什么这不是一种很好的实现方式。
        </p>

        <p>
            让我们从让请求处理程序返回需要在浏览器中显示的信息开始。我们需要将<em>requestHandler.js</em>修改为如下形式:
        </p>
        <pre class="prettyprint lang-js"><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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
                class="pln"> </span><span class="str">"Hello Start"</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"> upload</span><span class="pun">()</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
                class="pln">log</span><span class="pun">(</span><span
                class="str">"Request handler 'upload' was called."</span><span class="pun">);</span><span
                class="pln"><br>&nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span
                class="str">"Hello Upload"</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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <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">handle</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>&nbsp; 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>&nbsp; </span><span class="kwd">if</span><span
                class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
                class="pln"> handle</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"> </span><span class="str">'function'</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="kwd">return</span><span class="pln"> handle</span><span class="pun">[</span><span class="pln">pathname</span><span
                class="pun">]();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span class="pun">.</span><span
                class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
                class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span
                class="pln"> </span><span class="str">"404 Not found"</span><span class="pun">;</span><span class="pln"><br>&nbsp; </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>
            最后,我们需要对我们的<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></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"> handle</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; 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>&nbsp; &nbsp; </span><span class="kwd">var</span><span class="pln"> content </span><span
                class="pun">=</span><span class="pln"> route</span><span class="pun">(</span><span
                class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
                class="pun">)</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">content</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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>
            如果我们运行重构后的应用,一切都会工作的很好:请求<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>,浏览器会输出“Hello Start”,请求<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>会输出“Hello Upload”,而请求<a href="http://localhost:8888/foo" rel="nofollow">http://localhost:8888/foo</a> 会输出“404 Not found”。
        </p>

        <p>
            好,那么问题在哪里呢?简单的说就是: 当未来有请求处理程序需要进行非阻塞的操作的时候,我们的应用就“挂”了。
        </p>

        <p>
            没理解?没关系,下面就来详细解释下。
        </p>

        <a name="blocking-and-non-blocking"></a>

        <h4>阻塞与非阻塞</h4>

        <p>
            正如此前所提到的,当在请求处理程序中包括非阻塞操作时就会出问题。但是,在说这之前,我们先来看看什么是阻塞操作。
        </p>

        <p>
            我不想去解释“阻塞”和“非阻塞”的具体含义,我们直接来看,当在请求处理程序中加入阻塞操作时会发生什么。
        </p>

        <p>
            这里,我们来修改下<em>start</em>请求处理程序,我们让它等待10秒以后再返回“Hello Start”。因为,JavaScript中没有类似<em>sleep()</em>这样的操作,所以这里只能够来点小Hack来模拟实现。
        </p>

        <p>
            让我们将<em>requestHandlers.js</em>修改成如下形式:
        </p>
        <pre class="prettyprint lang-js"><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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">function</span><span
                class="pln"> sleep</span><span class="pun">(</span><span class="pln">milliSeconds</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="kwd">var</span><span class="pln"> startTime </span><span class="pun">=</span><span
                class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span
                class="typ">Date</span><span class="pun">().</span><span class="pln">getTime</span><span
                class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">while</span><span
                class="pln"> </span><span class="pun">(</span><span class="kwd">new</span><span
                class="pln"> </span><span class="typ">Date</span><span class="pun">().</span><span
                class="pln">getTime</span><span class="pun">()</span><span class="pln"> </span><span
                class="pun">&lt;</span><span class="pln"> startTime </span><span class="pun">+</span><span class="pln"> milliSeconds</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; sleep</span><span class="pun">(</span><span class="lit">10000</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
                class="pln"> </span><span class="str">"Hello Start"</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"> upload</span><span class="pun">()</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; console</span><span class="pun">.</span><span
                class="pln">log</span><span class="pun">(</span><span
                class="str">"Request handler 'upload' was called."</span><span class="pun">);</span><span
                class="pln"><br>&nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span
                class="str">"Hello Upload"</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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            上述代码中,当函数<em>start()</em>被调用的时候,Node.js会先等待10秒,之后才会返回“Hello Start”。当调用<em>upload()</em>的时候,会和此前一样立即返回。
        </p>

        <p>
            (当然了,这里只是模拟休眠10秒,实际场景中,这样的阻塞操作有很多,比方说一些长时间的计算操作等。)
        </p>

        <p>
            接下来就让我们来看看,我们的改动带来了哪些变化。
        </p>

        <p>
            如往常一样,我们先要重启下服务器。为了看到效果,我们要进行一些相对复杂的操作(跟着我一起做): 首先,打开两个浏览器窗口或者标签页。在第一个浏览器窗口的地址栏中输入<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>, 但是先不要打开它!
        </p>

        <p>
            在第二个浏览器窗口的地址栏中输入<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>, 同样的,先不要打开它!
        </p>

        <p>
           接下来,做如下操作:在第一个窗口中(“/start”)按下回车,然后快速切换到第二个窗口中(“/upload”)按下回车。
        </p>

        <p>
            注意,发生了什么: /start URL加载花了10秒,这和我们预期的一样。但是,/upload URL居然<em></em>花了10秒,而它在对应的请求处理程序中并没有类似于<em>sleep()</em>这样的操作!
        </p>

        <p>
            这到底是为什么呢?原因就是<em>start()</em>包含了阻塞操作。形象的说就是“它阻塞了所有其他的处理工作”。
        </p>

        <p>
            这显然是个问题,因为Node一向是这样来标榜自己的:<em>“在node中除了代码,所有一切都是并行执行的”</em>
        </p>

        <p>
            这句话的意思是说,Node.js可以在不新增额外线程的情况下,依然可以对任务进行并行处理 —— Node.js是单线程的。它通过事件轮询(event loop)来实现并行操作,对此,我们应该要充分利用这一点 —— 尽可能的避免阻塞操作,取而代之,多使用非阻塞操作。
        </p>

        <p>
            然而,要用非阻塞操作,我们需要使用回调,通过将函数作为参数传递给其他需要花时间做处理的函数(比方说,休眠10秒,或者查询数据库,又或者是进行大量的计算)。
        </p>

        <p>
            对于Node.js来说,它是这样处理的:<em>“嘿,probablyExpensiveFunction()(译者注:这里指的就是需要花时间处理的函数),你继续处理你的事情,我(Node.js线程)先不等你了,我继续去处理你后面的代码,请你提供一个callbackFunction(),等你处理完之后我会去调用该回调函数的,谢谢!”</em>
        </p>

        <p>
            (如果想要了解更多关于事件轮询细节,可以阅读Mixu的博文——<a href="http://blog.mixu.net/2011/02/01/understanding-the-node-js-event-loop/">理解node.js的事件轮询</a>。)
        </p>

        <p>
            接下来,我们会介绍一种错误的使用非阻塞操作的方式。
        </p>

        <p>
            和上次一样,我们通过修改我们的应用来暴露问题。
        </p>

        <p>
            这次我们还是拿<em>start</em>请求处理程序来“开刀”。将其修改成如下形式:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
                class="pun">).</span><span class="pln">exec</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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">var</span><span class="pln"> content </span><span
                class="pun">=</span><span class="pln"> </span><span class="str">"empty"</span><span class="pun">;</span><span
                class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span class="str">"ls -lah"</span><span
                class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
                class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span
                class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span
                class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; &nbsp; content </span><span class="pun">=</span><span
                class="pln"> stdout</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span
                class="pun">});</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">return</span><span
                class="pln"> content</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"> upload</span><span
                class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span
                class="pln"> </span><span class="str">"Hello Upload"</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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            上述代码中,我们引入了一个新的Node.js模块,<em>child_process</em>。之所以用它,是为了实现一个既简单又实用的非阻塞操作:<em>exec()</em>
        </p>

        <p>
            <em>exec()</em>做了什么呢?它从Node.js来执行一个shell命令。在上述例子中,我们用它来获取当前目录下所有的文件(“ls -lah”),然后,当<em>/start</em>URL请求的时候将文件信息输出到浏览器中。
        </p>

        <p>
            上述代码是非常直观的: 创建了一个新的变量<em>content</em>(初始值为“empty”),执行“ls -lah”命令,将结果赋值给content,最后将content返回。
        </p>

        <p>
            和往常一样,我们启动服务器,然后访问“<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>” 。
        </p>

        <p>
            之后会载入一个漂亮的web页面,其内容为“empty”。怎么回事?
        </p>

        <p>
            这个时候,你可能大致已经猜到了,<em>exec()</em>在非阻塞这块发挥了神奇的功效。它其实是个很好的东西,有了它,我们可以执行非常耗时的shell操作而无需迫使我们的应用停下来等待该操作。
        </p>

        <p>
            (如果想要证明这一点,可以将“ls -lah”换成比如“find /”这样更耗时的操作来效果)。
        </p>

        <p>
            然而,针对浏览器显示的结果来看,我们并不满意我们的非阻塞操作,对吧?
        </p>

        <p>
            好,接下来,我们来修正这个问题。在这过程中,让我们先来看看为什么当前的这种方式不起作用。
        </p>

        <p>
            问题就在于,为了进行非阻塞工作,<em>exec()</em>使用了回调函数。
        </p>

        <p>
            在我们的例子中,该回调函数就是作为第二个参数传递给<em>exec()</em>的匿名函数:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> </span><span
                class="pun">(</span><span class="pln">error</span><span class="pun">,</span><span
                class="pln"> stdout</span><span class="pun">,</span><span class="pln"> stderr</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; content </span><span
                class="pun">=</span><span class="pln"> stdout</span><span class="pun">;</span><span
                class="pln"><br></span><span class="pun">}</span></pre>

        <p>
            现在就到了问题根源所在了:我们的代码是同步执行的,这就意味着在调用<em>exec()</em>之后,Node.js会立即执行 <em>return content</em> ;在这个时候,<em>content</em>仍然是“empty”,因为传递给<em>exec()</em>的回调函数还未执行到——因为<em>exec()</em>的操作是异步的。
        </p>

        <p>
            我们这里“ls -lah”的操作其实是非常快的(除非当前目录下有上百万个文件)。这也是为什么回调函数也会很快的执行到 —— 不过,不管怎么说它还是异步的。
        </p>

        <p>
            为了让效果更加明显,我们想象一个更耗时的命令: “find /”,它在我机器上需要执行1分钟左右的时间,然而,尽管在请求处理程序中,我把“ls -lah”换成“find /”,当打开/start URL的时候,依然能够立即获得HTTP响应 —— 很明显,当<em>exec()</em>在后台执行的时候,Node.js自身会继续执行后面的代码。并且我们这里假设传递给<em>exec()</em>的回调函数,只会在“find /”命令执行完成之后才会被调用。
        </p>

        <p>
            那究竟我们要如何才能实现将当前目录下的文件列表显示给用户呢?
        </p>

        <p>
            好,了解了这种不好的实现方式之后,我们接下来来介绍如何以正确的方式让请求处理程序对浏览器请求作出响应。
        </p>

        <a name="responding-request-handlers-with-non-blocking-operations"></a>

        <h4>以非阻塞操作进行请求响应</h4>

        <p>
            我刚刚提到了这样一个短语 —— “正确的方式”。而事实上通常“正确的方式”一般都不简单。
        </p>

        <p>
            不过,用Node.js就有这样一种实现方案: 函数传递。下面就让我们来具体看看如何实现。
        </p>

        <p>
            到目前为止,我们的应用已经可以通过应用各层之间传递值的方式(请求处理程序 -&gt 请求路由 -&gt 服务器)将请求处理程序返回的内容(请求处理程序最终要显示给用户的内容)传递给HTTP服务器。
        </p>

        <p>
            现在我们采用如下这种新的实现方式:相对采用将内容传递给服务器的方式,我们这次采用将服务器“传递”给内容的方式。 从实践角度来说,就是将<em>response</em>对象(从服务器的回调函数<em>onRequest()</em>获取)通过请求路由传递给请求处理程序。 随后,处理程序就可以采用该对象上的函数来对请求作出响应。
        </p>

        <p>
            原理就是如此,接下来让我们来一步步实现这种方案。
        </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></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"> handle</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; route</span><span class="pun">(</span><span
                class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
                class="pun">,</span><span class="pln"> response</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span
                class="pun">}</span><span class="pln"><br><br>&nbsp; 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>&nbsp; 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>route()</em>函数获取返回值的做法,这次我们将response对象作为第三个参数传递给<em>route()</em>函数,并且,我们将<em>onRequest()</em>处理程序中所有有关<em>response</em>的函数调都移除,因为我们希望这部分工作让<em>route()</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">handle</span><span class="pun">,</span><span
                class="pln"> pathname</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>&nbsp; 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>&nbsp; </span><span class="kwd">if</span><span
                class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
                class="pln"> handle</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"> </span><span class="str">'function'</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; handle</span><span
                class="pun">[</span><span class="pln">pathname</span><span class="pun">](</span><span class="pln">response</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"> </span><span class="kwd">else</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span class="pun">.</span><span
                class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
                class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">writeHead</span><span class="pun">(</span><span class="lit">404</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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"404 Not found"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </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>
            同样的模式:相对此前从请求处理程序中获取返回值,这次取而代之的是直接传递<em>response</em>对象。
        </p>

        <p>
            如果没有对应的请求处理器处理,我们就直接返回“404”错误。
        </p>

        <p>
            最后,我们将<em>requestHandler.js</em>修改为如下形式:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
                class="pun">).</span><span class="pln">exec</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">response</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span
                class="str">"ls -lah"</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
                class="pln"> </span><span class="pun">(</span><span class="pln">error</span><span
                class="pun">,</span><span class="pln"> stdout</span><span class="pun">,</span><span
                class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; &nbsp; 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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">stdout</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </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"> upload</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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"Hello Upload"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>exports</span><span class="pun">.</span><span class="pln">start </span><span
                class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            我们的处理程序函数需要接收response参数,为了对请求作出直接的响应。
        </p>

        <p>
            <em>start</em>处理程序在<em>exec()</em>的匿名回调函数中做请求响应的操作,而<em>upload</em>处理程序仍然是简单的回复“Hello World”,只是这次是使用<em>response</em>对象而已。
        </p>

        <p>
            这时再次我们启动应用(<em>node index.js</em>),一切都会工作的很好。
        </p>

        <p>
            如果想要证明<em>/start</em>处理程序中耗时的操作不会阻塞对<em>/upload</em>请求作出立即响应的话,可以将<em>requestHandlers.js</em>修改为如下形式:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> exec </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"child_process"</span><span
                class="pun">).</span><span class="pln">exec</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">response</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; exec</span><span class="pun">(</span><span
                class="str">"find /"</span><span class="pun">,</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">{</span><span class="pln"> timeout</span><span class="pun">:</span><span
                class="pln"> </span><span class="lit">10000</span><span class="pun">,</span><span
                class="pln"> maxBuffer</span><span class="pun">:</span><span class="pln"> </span><span
                class="lit">20000</span><span class="pun">*</span><span class="lit">1024</span><span
                class="pln"> </span><span class="pun">},</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="kwd">function</span><span class="pln"> </span><span class="pun">(</span><span
                class="pln">error</span><span class="pun">,</span><span class="pln"> stdout</span><span
                class="pun">,</span><span class="pln"> stderr</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; 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>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">write</span><span class="pun">(</span><span
                class="pln">stdout</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </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"> upload</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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"Hello Upload"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>exports</span><span class="pun">.</span><span class="pln">start </span><span
                class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            这样一来,当请求<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>的时候,会花10秒钟的时间才载入,而当请求<a href="http://localhost:8888/upload" rel="nofollow">http://localhost:8888/upload</a>的时候,会立即响应,纵然这个时候/start响应还在处理中。
        </p>

        <a name="serving-something-useful"></a>

        <h3>更有用的场景</h3>

        <p>
            到目前为止,我们做的已经很好了,但是,我们的应用没有实际用途。
        </p>

        <p>
            服务器,请求路由以及请求处理程序都已经完成了,下面让我们按照此前的用例给网站添加交互:用户选择一个文件,上传该文件,然后在浏览器中看到上传的文件。 为了保持简单,我们假设用户只会上传图片,然后我们应用将该图片显示到浏览器中。
        </p>

        <p>
            好,下面就一步步来实现,鉴于此前已经对JavaScript原理性技术性的内容做过大量介绍了,这次我们加快点速度。
        </p>

        <p>
            要实现该功能,分为如下两步: 首先,让我们来看看如何处理POST请求(非文件上传),之后,我们使用Node.js的一个用于文件上传的外部模块。之所以采用这种实现方式有两个理由。
        </p>

        <p>
            第一,尽管在Node.js中处理基础的POST请求相对比较简单,但在这过程中还是能学到很多。
            <br>
            第二,用Node.js来处理文件上传(multipart POST请求)是比较复杂的,它<em></em>在本书的范畴,但,如何使用外部模块却是在本书涉猎内容之内。
        </p>

        <a name="handling-post-requests"></a>

        <h4>处理POST请求</h4>

        <p>
            考虑这样一个简单的例子:我们显示一个文本区(textarea)供用户输入内容,然后通过POST请求提交给服务器。最后,服务器接受到请求,通过处理程序将输入的内容展示到浏览器中。
        </p>
        
        <p>
            <em>/start</em>请求处理程序用于生成带文本区的表单,因此,我们将<em>requestHandlers.js</em>修改为如下形式:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
                class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;meta http-equiv="Content-Type" content="text/html; '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'charset=UTF-8" /&gt;'</span><span class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" method="post"&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="submit" value="Submit text" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/form&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/html&gt;'</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; 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/html"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">body</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; 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></span><span class="kwd">function</span><span class="pln"> upload</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>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"Hello Upload"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>exports</span><span class="pun">.</span><span class="pln">start </span><span
                class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            好了,现在我们的应用已经很完善了,都可以获得威比奖(Webby Awards)了,哈哈。(译者注:威比奖是由国际数字艺术与科学学院主办的评选全球最佳网站的奖项,具体参见详细说明)通过在浏览器中访问<a href="http://localhost:8888/start" rel="nofollow">http://localhost:8888/start</a>就可以看到简单的表单了,要记得重启服务器哦!
        </p>

        <p>
            你可能会说:这种直接将视觉元素放在请求处理程序中的方式太丑陋了。说的没错,但是,我并不想在本书中介绍诸如MVC之类的模式,因为这对于你了解JavaScript或者Node.js环境来说没多大关系。
        </p>

        <p>
            余下的篇幅,我们来探讨一个更有趣的问题: 当用户提交表单时,触发<em>/upload</em>请求处理程序处理POST请求的问题。
        </p>

        <p>
            现在,我们已经是新手中的专家了,很自然会想到采用异步回调来实现非阻塞地处理POST请求的数据。
        </p>

        <p>
            这里采用非阻塞方式处理是明智的,因为POST请求一般都比较“重” —— 用户可能会输入大量的内容。用阻塞的方式处理大数据量的请求必然会导致用户操作的阻塞。
        </p>

        <p>
            为了使整个过程非阻塞,Node.js会将POST数据拆分成很多小的数据块,然后通过触发特定的事件,将这些小数据块传递给回调函数。这里的特定的事件有<em>data</em>事件(表示新的小数据块到达了)以及<em>end</em>事件(表示所有的数据都已经接收完毕)。
        </p>

        <p>
            我们需要告诉Node.js当这些事件触发的时候,回调哪些函数。怎么告诉呢? 我们通过在<em>request</em>对象上注册<em>监听器</em>(listener) 来实现。这里的request对象是每次接收到HTTP请求时候,都会把该对象传递给<em>onRequest</em>回调函数。
        </p>

        <p>
            如下所示:
        </p>
        <pre class="prettyprint lang-js"><span class="pln">request</span><span class="pun">.</span><span class="pln">addListener</span><span
                class="pun">(</span><span class="str">"data"</span><span class="pun">,</span><span
                class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span
                class="pln">chunk</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </span><span class="com">// called when a new chunk of data was received</span><span
                class="pln"><br></span><span class="pun">});</span><span class="pln"><br><br>request</span><span
                class="pun">.</span><span class="pln">addListener</span><span class="pun">(</span><span class="str">"end"</span><span
                class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
                class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; </span><span
                class="com">// called when all chunks of data have been received</span><span
                class="pln"><br></span><span class="pun">});</span></pre>

        <p>
            问题来了,这部分逻辑写在哪里呢? 我们现在只是在服务器中获取到了<em>request</em>对象 —— 我们并没有像之前<em>response</em>对象那样,把 request 对象传递给请求路由和请求处理程序。
        </p>

        <p>
            在我看来,获取所有来自请求的数据,然后将这些数据给应用层处理,应该是HTTP服务器要做的事情。因此,我建议,我们直接在服务器中处理POST数据,然后将最终的数据传递给请求路由和请求处理器,让他们来进行进一步的处理。
        </p>

        <p>
            因此,实现思路就是: 将<em>data</em><em>end</em>事件的回调函数直接放在服务器中,在<em>data</em>事件回调中收集所有的POST数据,当接收到所有数据,触发<em>end</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></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"> handle</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </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>&nbsp; &nbsp; </span><span
                class="kwd">var</span><span class="pln"> postData </span><span class="pun">=</span><span
                class="pln"> </span><span class="str">""</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; request</span><span
                class="pun">.</span><span class="pln">setEncoding</span><span class="pun">(</span><span class="str">"utf8"</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; &nbsp; request</span><span
                class="pun">.</span><span class="pln">addListener</span><span class="pun">(</span><span class="str">"data"</span><span
                class="pun">,</span><span class="pln"> </span><span class="kwd">function</span><span
                class="pun">(</span><span class="pln">postDataChunk</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; &nbsp; &nbsp; postData </span><span class="pun">+=</span><span class="pln"> postDataChunk</span><span
                class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Received POST data chunk '"</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; postDataChunk </span><span
                class="pun">+</span><span class="pln"> </span><span class="str">"'."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">});</span><span
                class="pln"><br><br>&nbsp; &nbsp; request</span><span class="pun">.</span><span
                class="pln">addListener</span><span class="pun">(</span><span class="str">"end"</span><span class="pun">,</span><span
                class="pln"> </span><span class="kwd">function</span><span class="pun">()</span><span
                class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; &nbsp; &nbsp; route</span><span class="pun">(</span><span
                class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
                class="pun">,</span><span class="pln"> response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">});</span><span
                class="pln"><br><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; 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>&nbsp; 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>
            上述代码做了三件事情: 首先,我们设置了接收数据的编码格式为UTF-8,然后注册了“data”事件的监听器,用于收集每次接收到的新数据块,并将其赋值给<em>postData</em> 变量,最后,我们将请求路由的调用移到<em>end</em>事件处理程序中,以确保它只会当所有数据接收完毕后才触发,并且只触发一次。我们同时还把POST数据传递给请求路由,因为这些数据,请求处理程序会用到。
        </p>

        <p>
            上述代码在每个数据块到达的时候输出了日志,这对于最终生产环境来说,是很不好的(数据量可能会很大,还记得吧?),但是,在开发阶段是很有用的,有助于让我们看到发生了什么。
        </p>

        <p>
            我建议可以尝试下,尝试着去输入一小段文本,以及大段内容,当大段内容的时候,就会发现<em>data</em>事件会触发多次。
        </p>

        <p>
            再来点酷的。我们接下来在/upload页面,展示用户输入的内容。要实现该功能,我们需要将<em>postData</em>传递给请求处理程序,修改<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">handle</span><span class="pun">,</span><span
                class="pln"> pathname</span><span class="pun">,</span><span class="pln"> response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; 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>&nbsp; </span><span class="kwd">if</span><span
                class="pln"> </span><span class="pun">(</span><span class="kwd">typeof</span><span
                class="pln"> handle</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"> </span><span class="str">'function'</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; handle</span><span
                class="pun">[</span><span class="pln">pathname</span><span class="pun">](</span><span class="pln">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span
                class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"No request handler found for "</span><span
                class="pln"> </span><span class="pun">+</span><span class="pln"> pathname</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">writeHead</span><span class="pun">(</span><span class="lit">404</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>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"404 Not found"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; </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>
            然后,在<em>requestHandlers.js</em>中,我们将数据包含在对<em>upload</em>请求的响应中:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">function</span><span class="pln"> start</span><span
                class="pun">(</span><span class="pln">response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
                class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;meta http-equiv="Content-Type" content="text/html; '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'charset=UTF-8" /&gt;'</span><span class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" method="post"&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="submit" value="Submit text" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/form&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/html&gt;'</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; 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/html"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">body</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; 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></span><span class="kwd">function</span><span class="pln"> upload</span><span
                class="pun">(</span><span class="pln">response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"You've sent: "</span><span
                class="pln"> </span><span class="pun">+</span><span class="pln"> postData</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>exports</span><span class="pun">.</span><span class="pln">start </span><span
                class="pun">=</span><span class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            好了,我们现在可以接收POST数据并在请求处理程序中处理该数据了。
        </p>

        <p>
            我们最后要做的是: 当前我们是把请求的整个消息体传递给了请求路由和请求处理程序。我们应该只把POST数据中,我们感兴趣的部分传递给请求路由和请求处理程序。在我们这个例子中,我们感兴趣的其实只是<em>text</em>字段。
        </p>

        <p>
            我们可以使用此前介绍过的<em>querystring</em>模块来实现:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> querystring </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"querystring"</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">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
                class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;meta http-equiv="Content-Type" content="text/html; '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'charset=UTF-8" /&gt;'</span><span class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" method="post"&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="submit" value="Submit text" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/form&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/html&gt;'</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; 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/html"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">body</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; 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></span><span class="kwd">function</span><span class="pln"> upload</span><span
                class="pun">(</span><span class="pln">response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"You've sent the text: "</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; querystring</span><span class="pun">.</span><span
                class="pln">parse</span><span class="pun">(</span><span class="pln">postData</span><span
                class="pun">).</span><span class="pln">text</span><span class="pun">);</span><span class="pln"><br>&nbsp; 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>exports</span><span
                class="pun">.</span><span class="pln">start </span><span class="pun">=</span><span
                class="pln"> start</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span></pre>

        <p>
            好了,以上就是关于处理POST数据的全部内容。
        </p>

        <a name="handling-file-uploads"></a>

        <h4>处理文件上传</h4>

        <p>
            最后,我们来实现我们最终的用例:允许用户上传图片,并将该图片在浏览器中显示出来。
        </p>

        <p>
            回到90年代,这个用例完全可以满足用于IPO的商业模型了,如今,我们通过它能学到这样两件事情: 如何安装外部Node.js模块,以及如何将它们应用到我们的应用中。
        </p>

        <p>
            这里我们要用到的外部模块是Felix Geisendörfer开发的<em>node-formidable</em>模块。它对解析上传的文件数据做了很好的抽象。 其实说白了,处理文件上传<em>“就是”</em>处理POST数据 —— 但是,麻烦的是在具体的处理细节,所以,这里采用现成的方案更合适点。
        </p>

        <p>
            使用该模块,首先需要安装该模块。Node.js有它自己的包管理器,叫<em>NPM</em>。它可以让安装Node.js的外部模块变得非常方便。通过如下一条命令就可以完成该模块的安装:
        </p>
        <pre class="prettyprint lang-bash"><span class="pln">npm install formidable</span></pre>

        <p>
            如果终端输出如下内容:
        </p>
        <pre class="prettyprint lang-bash"><span class="pln">npm info build </span><span class="typ">Success</span><span
                class="pun">:</span><span class="pln"> formidable@1</span><span class="pun">.</span><span class="lit">0.9</span><span
                class="pln"><br>npm ok</span></pre>

        <p>
            就说明模块已经安装成功了。
        </p>

        <p>
            现在我们就可以用<em>formidable</em>模块了——使用外部模块与内部模块类似,用require语句将其引入即可:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> formidable </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"formidable"</span><span
                class="pun">);</span></pre>

        <p>
            这里该模块做的就是将通过HTTP POST请求提交的表单,在Node.js中可以被解析。我们要做的就是创建一个新的<em>IncomingForm</em>,它是对提交表单的抽象表示,之后,就可以用它解析request对象,获取表单中需要的数据字段。
        </p>

        <p>
            node-formidable官方的例子展示了这两部分是如何融合在一起工作的:
        </p>
        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> formidable </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">'formidable'</span><span
                class="pun">),</span><span class="pln"><br>&nbsp; &nbsp; 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>&nbsp; &nbsp; sys </span><span class="pun">=</span><span
                class="pln"> require</span><span class="pun">(</span><span class="str">'sys'</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">req</span><span class="pun">,</span><span class="pln"> res</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span
                class="pln"><br>&nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span
                class="pun">(</span><span class="pln">req</span><span class="pun">.</span><span
                class="pln">url </span><span class="pun">==</span><span class="pln"> </span><span
                class="str">'/upload'</span><span class="pln"> </span><span class="pun">&amp;&amp;</span><span
                class="pln"> req</span><span class="pun">.</span><span class="pln">method</span><span
                class="pun">.</span><span class="pln">toLowerCase</span><span class="pun">()</span><span
                class="pln"> </span><span class="pun">==</span><span class="pln"> </span><span class="str">'post'</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="com">// parse a file upload</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">var</span><span
                class="pln"> form </span><span class="pun">=</span><span class="pln"> </span><span
                class="kwd">new</span><span class="pln"> formidable</span><span class="pun">.</span><span class="typ">IncomingForm</span><span
                class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; form</span><span class="pun">.</span><span
                class="pln">parse</span><span class="pun">(</span><span class="pln">req</span><span class="pun">,</span><span
                class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span
                class="pln">err</span><span class="pun">,</span><span class="pln"> fields</span><span
                class="pun">,</span><span class="pln"> files</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; res</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>&nbsp; &nbsp; &nbsp; res</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">'received upload:\n\n'</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; res</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">(</span><span
                class="pln">sys</span><span class="pun">.</span><span class="pln">inspect</span><span
                class="pun">({</span><span class="pln">fields</span><span class="pun">:</span><span
                class="pln"> fields</span><span class="pun">,</span><span class="pln"> files</span><span
                class="pun">:</span><span class="pln"> files</span><span class="pun">}));</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">});</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span
                class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span
                class="com">// show a file upload form</span><span class="pln"><br>&nbsp; res</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/html'</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; res</span><span class="pun">.</span><span class="pln">end</span><span
                class="pun">(</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" enctype="multipart/form-data" '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'method="post"&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="text" name="title"&gt;&lt;br&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="file" name="upload" multiple="multiple"&gt;&lt;br&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="submit" value="Upload"&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/form&gt;'</span><span class="pln"><br>&nbsp; </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>node</em>来执行,就可以进行简单的表单提交了,包括文件上传。然后,可以看到通过调用<em>form.parse</em>传递给回调函数的<em>files</em>对象的内容,如下所示:
        </p>
        <pre class="lang-js">received upload:

{ fields: { title: 'Hello World' },
  files:
   { upload:
      { size: 1558,
        path: '/tmp/1c747974a27a6292743669e91f29350b',
        name: 'us-flag.png',
        type: 'image/png',
        lastModifiedDate: Tue, 21 Jun 2011 07:02:41 GMT,
        _writeStream: [Object],
        length: [Getter],
        filename: [Getter],
        mime: [Getter] } } }</pre>

        <p>
            为了实现我们的功能,我们需要将上述代码应用到我们的应用中,另外,我们还要考虑如何将上传文件的内容(保存在<em>/tmp</em>目录中)显示到浏览器中。
        </p>

        <p>
            我们先来解决后面那个问题: 对于保存在本地硬盘中的文件,如何才能在浏览器中看到呢?
        </p>

        <p>
            显然,我们需要将该文件读取到我们的服务器中,使用一个叫<em>fs</em>的模块。
        </p>

        <p>
            我们来添加<em>/show</em>URL的请求处理程序,该处理程序直接硬编码将文件<em>/tmp/test.png</em>内容展示到浏览器中。当然了,首先需要将该图片保存到这个位置才行。
        </p>

        <p>
            将<em>requestHandlers.js</em>修改为如下形式:
        </p>

        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> querystring </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"querystring"</span><span
                class="pun">),</span><span class="pln"><br>&nbsp; &nbsp; fs </span><span class="pun">=</span><span
                class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</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">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
                class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;meta http-equiv="Content-Type" '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'content="text/html; charset=UTF-8" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" method="post"&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;textarea name="text" rows="20" cols="60"&gt;&lt;/textarea&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;input type="submit" value="Submit text" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/form&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/html&gt;'</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; 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/html"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">body</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; 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></span><span class="kwd">function</span><span class="pln"> upload</span><span
                class="pun">(</span><span class="pln">response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"You've sent the text: "</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; querystring</span><span class="pun">.</span><span
                class="pln">parse</span><span class="pun">(</span><span class="pln">postData</span><span
                class="pun">).</span><span class="pln">text</span><span class="pun">);</span><span class="pln"><br>&nbsp; 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></span><span class="kwd">function</span><span
                class="pln"> show</span><span class="pun">(</span><span class="pln">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'show' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; fs</span><span class="pun">.</span><span class="pln">readFile</span><span
                class="pun">(</span><span class="str">"/tmp/test.png"</span><span class="pun">,</span><span
                class="pln"> </span><span class="str">"binary"</span><span class="pun">,</span><span
                class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span
                class="pln">error</span><span class="pun">,</span><span class="pln"> file</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="kwd">if</span><span class="pun">(</span><span class="pln">error</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
                class="lit">500</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>&nbsp; &nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">error </span><span
                class="pun">+</span><span class="pln"> </span><span class="str">"\n"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; 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">"image/png"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">file</span><span
                class="pun">,</span><span class="pln"> </span><span class="str">"binary"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">}</span><span class="pln"><br>&nbsp; </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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">show </span><span class="pun">=</span><span
                class="pln"> show</span><span class="pun">;</span></pre>

        <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></span><span class="kwd">var</span><span
                class="pln"> router </span><span class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span
                class="str">"./router"</span><span class="pun">);</span><span class="pln"><br></span><span class="kwd">var</span><span
                class="pln"> requestHandlers </span><span class="pun">=</span><span class="pln"> require</span><span
                class="pun">(</span><span class="str">"./requestHandlers"</span><span class="pun">);</span><span
                class="pln"><br><br></span><span class="kwd">var</span><span class="pln"> handle </span><span
                class="pun">=</span><span class="pln"> </span><span class="pun">{}</span><span
                class="pln"><br>handle</span><span class="pun">[</span><span class="str">"/"</span><span
                class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
                class="pun">[</span><span class="str">"/start"</span><span class="pun">]</span><span
                class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">start</span><span class="pun">;</span><span class="pln"><br>handle</span><span
                class="pun">[</span><span class="str">"/upload"</span><span class="pun">]</span><span
                class="pln"> </span><span class="pun">=</span><span class="pln"> requestHandlers</span><span
                class="pun">.</span><span class="pln">upload</span><span class="pun">;</span><span class="pln"><br>handle</span><span
                class="pun">[</span><span class="str">"/show"</span><span class="pun">]</span><span class="pln"> </span><span
                class="pun">=</span><span class="pln"> requestHandlers</span><span class="pun">.</span><span
                class="pln">show</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><span
                class="pln">router</span><span class="pun">.</span><span class="pln">route</span><span
                class="pun">,</span><span class="pln"> handle</span><span class="pun">);</span></pre>

        <p>
            重启服务器之后,通过访问<a href="http://localhost:8888/show" rel="nofollow">http://localhost:8888/show</a>,就可以看到保存在<em>/tmp/test.png</em>的图片了。
        </p>

        <p>
            好,最后我们要的就是:
        </p>
        
        <p>
            <ul>
                <li>
                    在<em>/start</em>表单中添加一个文件上传元素
                </li>
                <li>
                    将node-formidable整合到我们的<em>upload</em>请求处理程序中,用于将上传的图片保存到<em>/tmp/test.png</em>
                </li>
        
                <li>
                    将上传的图片内嵌到<em>/upload</em>URL输出的HTML中
                </li>
            </ul>
        </p>

        <p>
            第一项很简单。只需要在HTML表单中,添加一个<em>multipart/form-data</em>的编码类型,移除此前的文本区,添加一个文件上传组件,并将提交按钮的文案改为“Upload file”即可。 如下<em>requestHandler.js</em>所示:
        </p>

        <pre class="prettyprint lang-js"><span class="kwd">var</span><span class="pln"> querystring </span><span
                class="pun">=</span><span class="pln"> require</span><span class="pun">(</span><span class="str">"querystring"</span><span
                class="pun">),</span><span class="pln"><br>&nbsp; &nbsp; fs </span><span class="pun">=</span><span
                class="pln"> require</span><span class="pun">(</span><span class="str">"fs"</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">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'start' was called."</span><span
                class="pun">);</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">var</span><span
                class="pln"> body </span><span class="pun">=</span><span class="pln"> </span><span class="str">'&lt;html&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;meta http-equiv="Content-Type" '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'content="text/html; charset=UTF-8" /&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/head&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;body&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;form action="/upload" enctype="multipart/form-data" '</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'method="post"&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;input type="file" name="upload"&gt;'</span><span class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;input type="submit" value="Upload file" /&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/form&gt;'</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="str">'&lt;/body&gt;'</span><span class="pun">+</span><span
                class="pln"><br>&nbsp; &nbsp; </span><span class="str">'&lt;/html&gt;'</span><span
                class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; 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/html"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">body</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; 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></span><span class="kwd">function</span><span class="pln"> upload</span><span
                class="pun">(</span><span class="pln">response</span><span class="pun">,</span><span class="pln"> postData</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'upload' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; 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>&nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="str">"You've sent the text: "</span><span
                class="pun">+</span><span class="pln"><br>&nbsp; querystring</span><span class="pun">.</span><span
                class="pln">parse</span><span class="pun">(</span><span class="pln">postData</span><span
                class="pun">).</span><span class="pln">text</span><span class="pun">);</span><span class="pln"><br>&nbsp; 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></span><span class="kwd">function</span><span
                class="pln"> show</span><span class="pun">(</span><span class="pln">response</span><span
                class="pun">,</span><span class="pln"> postData</span><span class="pun">)</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; console</span><span
                class="pun">.</span><span class="pln">log</span><span class="pun">(</span><span class="str">"Request handler 'show' was called."</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; fs</span><span class="pun">.</span><span class="pln">readFile</span><span
                class="pun">(</span><span class="str">"/tmp/test.png"</span><span class="pun">,</span><span
                class="pln"> </span><span class="str">"binary"</span><span class="pun">,</span><span
                class="pln"> </span><span class="kwd">function</span><span class="pun">(</span><span
                class="pln">error</span><span class="pun">,</span><span class="pln"> file</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="kwd">if</span><span class="pun">(</span><span class="pln">error</span><span
                class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">writeHead</span><span class="pun">(</span><span
                class="lit">500</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>&nbsp; &nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">error </span><span
                class="pun">+</span><span class="pln"> </span><span class="str">"\n"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">}</span><span class="pln"> </span><span class="kwd">else</span><span
                class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; 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">"image/png"</span><span class="pun">});</span><span
                class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span class="pun">.</span><span
                class="pln">write</span><span class="pun">(</span><span class="pln">file</span><span
                class="pun">,</span><span class="pln"> </span><span class="str">"binary"</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; response</span><span
                class="pun">.</span><span class="pln">end</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span
                class="pun">}</span><span class="pln"><br>&nbsp; </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><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">upload </span><span class="pun">=</span><span
                class="pln"> upload</span><span class="pun">;</span><span class="pln"><br>exports</span><span
                class="pun">.</span><span class="pln">show </span><span class="pun">=</span><span
                class="pln"> show</span><span class="pun">;</span></pre>

        <p>
            很好。下一步相对比较复杂。这里有这样一个问题: 我们需要在<em>upload</em>处理程序中对上传的文件进行处理,这样的话,我们就需要将<em>request</em>对象传递给node-formidable的<em>form.parse</em>函数。
        </p>

        <p>
            但是,我们有的只是<em>response</em>对象和<em>postData</em>数组。看样子,我们只能不得不将<em>request</em>对象从服务器开始一路通过请求路由,再传递给请求处理程序。 或许还有更好的方案,但是,不管怎么说,目前这样做可以满足我们的需求。
        </p>

        <p>
            到这里,我们可以将<em>postData</em>从服务器以及请求处理程序中移除了 —— 一方面,对于我们处理文件上传来说已经不需要了,另外一方面,它甚至可能会引发这样一个问题: 我们已经“消耗”了<em>request</em>对象中的数据,这意味着,对于<em>form.parse</em>来说,当它想要获取数据的时候就什么也获取不到了。(因为Node.js不会对数据做缓存)
        </p>

        <p>
            我们从<em>server.js</em>开始 —— 移除对postData的处理以及<em>request.setEncoding</em>
            (这部分node-formidable自身会处理),转而采用将<em>request</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"> handle</span><span class="pun">)</span><span class="pln"> </span><span
                class="pun">{</span><span class="pln"><br>&nbsp; </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>&nbsp; &nbsp; </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>&nbsp; &nbsp; 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>&nbsp; &nbsp; route</span><span class="pun">(</span><span
                class="pln">handle</span><span class="pun">,</span><span class="pln"> pathname</span><span
                class="pun">,</span><span class="pln"> response</span><span class="pun">,</span><span class="pln"> request</span><span
                class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span
                class="pln"><br><br>&nbsp; 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>&nbsp; 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&quo