ホームページ  >  記事  >  ウェブフロントエンド  >  Node.js入門チュートリアルミニブック、node.js入門Webアプリケーション開発完全例_基礎知識

Node.js入門チュートリアルミニブック、node.js入門Webアプリケーション開発完全例_基礎知識

WBOY
WBOYオリジナル
2016-05-16 16:53:09919ブラウズ

本书状态

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

本书中的代码案例都在Node.js 0.6.11版本中测试过,可以正确工作。

读者对象

本书最适合与我有相似技术背景的读者: 至少对一门诸如Ruby、Python、PHP或者Java这样面向对象的语言有一定的经验;对JavaScript处于初学阶段,并且完全是一个Node.js的新手。

这里指的适合对其他编程语言有一定经验的开发者,意思是说,本书不会对诸如数据类型、变量、控制结构等等之类非常基础的概念作介绍。要读懂本书,这些基础的概念我都默认你已经会了。

然而,本书还是会对JavaScript中的函数和对象作详细介绍,因为它们与其他同类编程语言中的函数和对象有很大的不同。

本书结构

读完本书之后,你将完成一个完整的web应用,该应用允许用户浏览页面以及上传文件。

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

本书先从介绍在Node.js环境中进行JavaScript开发和在浏览器环境中进行JavaScript开发的差异开始。

紧接着,会带领大家完成一个最传统的“Hello World”应用,这也是最基础的Node.js应用。

最后,会和大家讨论如何设计一个“真正”完整的应用,剖析要完成该应用需要实现的不同模块,并一步一步介绍如何来实现这些模块。

可以确保的是,在这过程中,大家会学到JavaScript中一些高级的概念、如何使用它们以及为什么使用这些概念就可以实现而其他编程语言中同类的概念就无法实现。

该应用所有的源代码都可以通过 本书Github代码仓库:https://github.com/ManuelKiessling/NodeBeginnerBook/tree/master/code/application.

JavaScript与Node.js

JavaScript与你

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

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

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

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

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

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

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

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

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

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

本书的目标就是给你提供指导。

简短申明

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

私は前のセクションで説明した人物です。私はバックエンド Web アプリケーションの開発には慣れていましたが、「実際の」JavaScript と Node.js については初めてでした。私は JavaScript の高度な概念を最近学んだばかりで、実践的な経験はありません。

したがって、この本は「入門からマスターまで」の本ではなく、「初心者から上級エントリーまで」の本に近いです。

成功すれば、この本は私が Node.js を学び始めたときに最も手に入れたかったチュートリアルになるでしょう。

サーバーサイド JavaScript

JavaScript は最初はブラウザーで実行されましたが、ブラウザーは JavaScript を使用して何ができるかを定義するコンテキストを提供するだけで、JavaScript 言語自体が何ができるかについてはあまり「説明」しませんでした。実際、JavaScript は「完全な」言語です。さまざまなコンテキストで使用でき、その機能は他の同様の言語と同等です。

Node.js は実際には、JavaScript コードをバックエンド (ブラウザ環境外) で実行できるようにする別のコンテキストです。

バックグラウンドで JavaScript コードを実行するには、まずコードが解釈されてから、正しく実行される必要があります。これが Node.js の原理です。Node.js は、Google の V8 仮想マシン (Google の Chrome ブラウザで使用される JavaScript 実行環境) を使用して JavaScript コードを解釈し、実行します。

さらに、Node.js には、端末への文字列の出力など、多くの反復的なタスクを簡素化できる便利なモジュールが多数付属しています。

したがって、Node.js は実際にはランタイム環境とライブラリの両方です。

Node.js を使用するには、まず Node.js をインストールする必要があります。 Node.js のインストール方法については、ここでは詳しく説明しません。公式のインストール ガイドを直接参照してください。インストールが完了したら、引き続き戻ってこの本の残りの部分を読んでください。

「ハローワールド」

さて、これ以上「ナンセンス」はやめて、最初の Node.js アプリケーション「Hello World」を開始しましょう。

お気に入りのエディターを開き、helloworld.js ファイルを作成します。行う必要があるのは、「Hello World」を STDOUT に出力することです。この関数を実装するコードは次のとおりです。

コードをコピーします コードは次のとおりです:
console.log("Hello World");
ファイルを保存し、Node.js を通じて実行します:

コードをコピーします コードは次のとおりです:
node helloworld.js

正常であればターミナルにHello Worldが出力されます。

わかりました、このアプリケーションが少し退屈であることは認めます。それでは、「乾いたもの」をいくつか手に入れましょう。

Node.js に基づく完全な Web アプリケーション

ユースケース

目標をシンプルですが、十分現実的なものにしましょう:

1. ユーザーはブラウザを通じてアプリケーションを使用できます。
2. ユーザーが http://domain/start をリクエストすると、ファイル アップロード フォームのあるウェルカム ページが表示されます。
3. ユーザーは画像を選択してフォームを送信すると、ファイルが http://domain/upload にアップロードされ、アップロードが完了すると画像がページに表示されます。
それでは、Google にアクセスして、機能を完成させるために何かを見つけることもできます。しかし、今はそれはやめましょう。

さらに、この目標を達成する過程では、コードがエレガントであるかどうかに関係なく、単なる基本的なコード以上のものが必要です。また、より複雑な Node.js アプリケーションを構築する方法を見つけるために、これを抽象化する必要があります。

分析にさまざまなモジュールを適用する

上記のユースケースを実装するには、どの部分を実装する必要があるでしょうか?

1. Web ページを提供する必要があるため、HTTP サーバーが必要です
2. さまざまなリクエストに対して、サーバーはリクエストされた URL に応じてさまざまな応答を返す必要があるため、リクエストをルーティングするためのルートが必要ですリクエスト ハンドラー
3. リクエストがサーバーによって受信され、ルートを通過した後、処理する必要があるため、最後のリクエスト ハンドラー
4. ルートでもそれを処理できる必要があります。データを POST し、そのデータをよりわかりやすい形式にカプセル化してリクエスト処理プログラムに渡すため、データ処理関数をリクエストする必要があります
5. URL に対応するリクエストを処理するだけでなく、コンテンツを表示します。つまり、リクエスト ハンドラーがユーザーのブラウザにコンテンツを送信するには、いくつかの表示ロジックが必要です
6. 最後に、ユーザーは画像をアップロードする必要があるため、この
の詳細を処理するアップロード処理関数が必要です。 🎜> まずやってみましょう PHP を使用してこの構造を構築する方法を考えてみましょう。通常、Apache HTTP サーバーと mod_php5 モジュールを使用します。
この観点から見ると、「HTTP リクエストを受信して​​ Web ページを提供する」という要件全体を PHP で処理する必要はまったくありません。

しかし、Node.js の場合、概念はまったく異なります。 Node.js を使用する場合、アプリケーションを実装するだけでなく、HTTP サーバー全体も実装します。実際、当社の Web アプリケーションと対応する Web サーバーは基本的に同じです。

やるべきことがたくさんあるように思えますが、Node.js にとってそれは面倒なことではないことが徐々にわかるでしょう。

それでは、最初の部分である HTTP サーバーから実装への道を始めましょう。

アプリケーションを構築するためのモジュール

基本的な HTTP サーバー

私が初めての「本物の」Node.js アプリケーションを書き始めようとしたとき、Node.js コードの書き方がわからなかっただけでなく、それを整理する方法もわかりませんでした。
すべてを 1 つのファイルに入れるべきですか?インターネット上には、Node.js で記述された基本的な HTTP サーバーにすべてのロジックを組み込む方法を説明するチュートリアルが数多くあります。しかし、コードを読みやすい状態に保ちながら、さらにコンテンツを追加したい場合はどうすればよいでしょうか?

実際、異なる関数のコードを異なるモジュールに配置する限り、コードの分離を維持するのは非常に簡単です。

このアプローチでは、Node.js で実行できるクリーンなメイン ファイルを作成でき、メイン ファイルや他のモジュールから呼び出すことができるクリーンなモジュールを作成できます。

それでは、アプリケーションを開始するメイン ファイルと、HTTP サーバー コードを保持するモジュールを作成しましょう。

私の考えでは、メイン ファイルをindex.jsと呼ぶのは、多かれ少なかれ標準的な形式です。サーバーモジュールをserver.jsというファイルに入れるとわかりやすいです。

サーバーモジュールから始めましょう。プロジェクトのルート ディレクトリに server.js というファイルを作成し、次のコードを記述します。

コードをコピー コードは次のようになります。

var http = require("http");

http.createServer(function(request,response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World") ;
応答.end();
}).listen(8888);


完成しました!これで、動作する HTTP サーバーが完成しました。これを証明するために、このコードを実行してテストしてみましょう。まず、Node.js を使用してスクリプトを実行します。

node server.js
次に、ブラウザを開いて http://localhost:8888/ にアクセスすると、「Hello World」と書かれた Web ページが表示されます。

それは興味深いですね。プロジェクトの構成方法はさておき、まず HTTP サーバーについて話しましょう。どう思いますか?それは後で解決することを約束します。

HTTP サーバーを分析する

それでは次に、この HTTP サーバーの構成を分析してみましょう。

最初の行は、Node.js に付属する http モジュールを要求し、それを http 変数に割り当てます。

次に、http モジュールによって提供される関数 createServer を呼び出します。この関数はオブジェクトを返します。このオブジェクトには listen というメソッドがあり、HTTP サーバーがリッスンしているポート番号を指定する数値パラメーターがあります。

ここでは http.createServer の括弧内の関数定義を無視しましょう。

次のようなコードを使用してサーバーを起動し、ポート 8888 でリッスンすることもできます:

コードをコピーします コードは次のとおりです。

var http = require("http");

var server = http.createServer();
server.listen(8888);


このコードは、ポート 8888 でリッスンするサーバーを起動するだけです。それ以外のことは何も行いません。リクエストは答えられます。

最も興味深い (そして、PHP のような保守的な言語に慣れている人にとっては奇妙な) 部分は、createSever() の最初の引数である関数定義です。

実際、この関数定義は createServer() の最初で唯一のパラメータです。 JavaScript では、関数を他の変数と同様に渡すことができるためです。

関数転送を実行します

たとえば、これを行うことができます:

コードをコピーします コードは次のとおりです:

関数 Say(word) {
console.log(word);
}

関数実行(someFunction, value) {
someFunction(value);
}

execute(say, "Hello");
このコードをよく読んでください。ここでは、say 関数を実行関数の最初の変数として渡します。ここで返されるのは、say の戻り値ではなく、say 自体です。

このようにして、say は、execute のローカル変数 someFunction になり、someFunction() (括弧付き) を呼び出すことで、say 関数を使用できます。

もちろん、say には変数があるので、execute は someFunction を呼び出すときにそのような変数を渡すことができます。

先ほどと同様に、関数を名前の変数として渡すことができます。ただし、この「最初に定義してから渡す」というサイクルを回避する必要はなく、この関数を別の関数の括弧内で直接定義して渡すことができます:

Copyコード コードは次のとおりです:

functionexecute(someFunction, value) {
someFunction(value);
}

execute(function(word){ console.log(word) }, "Hello");
execute が最初のパラメータを受け入れる場所で、execute に渡す関数を直接定義します。

この方法では、関数に名前を付ける必要さえないため、この関数は匿名関数と呼ばれます。

これは、私が「高度な」JavaScript と考えるものに初めて遭遇しましたが、それでも段階的に進める必要があります。ここでは、これを受け入れましょう。JavaScript では、関数は別の関数としてパラメーターを受け取ることができます。最初に関数を定義してからそれを渡すことも、パラメーターが渡される場所で直接関数を定義することもできます。

関数の受け渡しによって HTTP サーバーが機能する仕組み

この知識を基に、単純ではあるが単純ではない HTTP サーバーを見てみましょう:

コードをコピー コードは次のとおりです。

var http = require("http");

http.createServer(function(request,response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World") ;
応答.end();
}).listen(8888);


これで、はるかに明確になるはずです。匿名関数を createServer 関数に渡しました。

次のようなコードでも同じ目的を達成できます:

コードをコピーします コードは次のとおりです:

var http = require("http");

function onRequest(request,response) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
応答.end();
}

http.createServer(onRequest).listen(8888);


ここで、次の質問をする必要があるかもしれません。なぜこのメソッドを使用するのでしょうか。

イベント駆動型コールバック

この質問に答えるのは (少なくとも私にとっては) 簡単ではありませんが、これが Node.js のネイティブな動作方法です。イベント駆動型なので非常に高速です。

Felix Geisendörfer の傑作『Understanding Node.js』を少し読んでみると、背景知識が得られます。

それはすべて、「Node.js はイベント駆動型である」という事実に帰着します。うーん、実はこの文が何を意味するのか正確には分かりません。しかし、Node.js を使用して Web ベースのアプリケーションを作成することがなぜ理にかなっているのかを説明していきます。

http.createServer メソッドを使用する場合、もちろん、サーバーが特定のポートをリッスンするだけでなく、サーバーが HTTP リクエストを受信したときに何かを実行することも必要です。

問題は、これが非同期であることです。リクエストはいつでも到着する可能性がありますが、サーバーは単一プロセスで実行されています。

PHP アプリケーションを作成するときは、このことについてまったく心配しません。リクエストが受信されるたびに、Web サーバー (通常は Apache) がこのリクエスト用の新しいプロセスを作成し、対応するプロセスを最初から最後まで実行し始めます。 PHPスクリプト。

では、Node.js プログラムでは、新しいリクエストがポート 8888 に到着したとき、どのようにプロセスを制御するのでしょうか?

そうですね、これは Node.js/JavaScript のイベント駆動設計が本当に役立つところです。ただし、それを習得するにはまだいくつかの新しい概念を学ぶ必要があります。これらの概念がサーバー コードにどのように適用されるかを見てみましょう。

サーバーを作成し、それを作成したメソッドに関数を渡しました。サーバーがリクエストを受信するたびに、この関数が呼び出されます。

これがいつ起こるかはわかりませんが、リクエストを処理する場所ができました。リクエストを渡した関数です。事前定義関数であるか匿名関数であるかは関係ありません。

これは伝説のコールバックです。関数をメソッドに渡し、このメソッドはこの関数を呼び出して、対応するイベントが発生したときにコールバックを実行します。

少なくとも私にとっては、それを理解するのに多少の努力が必要でした。それでもよくわからない場合は、Felix のブログ投稿を読んでください。

この新しい概念をもう一度考えてみましょう。サーバーを作成した後、HTTP リクエストが受信されず、コールバック関数が呼び出されない場合でも、コードが引き続き有効であることをどのように証明すればよいでしょうか?これを試してみましょう:

コードをコピーします コードは次のとおりです:

var http = require ("http ");

function onRequest(request, response) {
console.log("リクエストを受信しました。");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);

console.log("サーバーが起動しました。");


注: onRequest (コールバック関数) がトリガーされる場合、console.log を使用してテキストを出力します。 HTTP サーバーが動作し始めると、テキストも出力されます。

いつものようにnodeserver.jsを実行すると、すぐにコマンドラインに「サーバーが起動しました。」と出力されます。サーバーにリクエストを送信すると (ブラウザで http://localhost:8888/ にアクセスすると)、コマンド ラインに「リクエストを受け取りました」というメッセージが表示されます。

これはイベント駆動型の非同期サーバーサイド JavaScript とそのコールバックです。

(サーバー上の Web ページにアクセスすると、サーバーは「リクエストを受信しました。」を 2 回出力する場合があることに注意してください。これは、http://localhost:8888/ Get http にアクセスすると、ほとんどのサーバーが読み取りを試みるためです。 ://localhost:8888/favicon.ico )

サーバーがリクエストを処理する方法

それでは、サーバー コードの残りの部分、つまりコールバック関数 onRequest() の主要部分を簡単に分析してみましょう。

コールバックが開始され、onRequest() 関数がトリガーされると、リクエストとレスポンスの 2 つのパラメーターが渡されます。

これらは、HTTP リクエストの詳細を処理し、リクエストに応答する (リクエスト元のブラウザに何かを送り返すなど) ために使用できるメソッドを持つオブジェクトです。

私たちのコードは次のようになります: リクエストを受信したときに、response.writeHead() 関数を使用して HTTP ステータス 200 と HTTP ヘッダーのコンテンツ タイプ (content-type) を送信し、response.write() を使用します。 HTTP 対応ボディを追加する関数 「Hello World」というテキストを送信します。

最後に、response.end() を呼び出して応答を完了します。

今のところ、リクエストの詳細は気にしないので、リクエスト オブジェクトは使用しません。

サーバーモジュールを配置する場所

OK、約束したように、アプリケーションの整理方法に戻りましょう。これで、server.js ファイルに非常に基本的な HTTP サーバー コードが作成されました。通常、アプリケーションの他のモジュール (server.js Boot の HTTP サーバー モジュールなど) を呼び出す、index.js というファイルがあると述べました。そしてアプリケーションを起動します。

ここで、server.js を実際の Node.js モジュールに変換して、(まだ開始されていない) Index.js メイン ファイルで使用できるようにする方法について説明しましょう。

コード内でモジュールを使用していることに気づいたかもしれません。次のように:

コードをコピー コードは次のとおりです:

var http = require(" http") ;

...

http.createServer(...);


Node.js には「http」というモジュールが付属しており、コード内でそれをリクエストし、戻り値をローカル変数に割り当てます。

これにより、ローカル変数が、http モジュールによって提供されるすべてのパブリック メソッドを備えたオブジェクトに変わります。

このようなローカル変数にはモジュール名と同じ名前を付けるのが慣例ですが、好みに合わせて使用​​することもできます:

コードをコピー コードは次のとおりです:

var foo = require("http");

...

foo.createServer(...);


非常に良いですね。Node.js 内部モジュールの使用方法はすでに明確です。独自のモジュールをどのように作成し、どのように使用するのでしょうか?

server.js を実際のモジュールに変換すると、それがわかります。

実際には、あまり多くの変更を加える必要はありません。コードの一部をモジュールに変換するということは、モジュールを要求するスクリプトに提供したい機能の部分をエクスポートする必要があることを意味します。

現時点では、HTTP サーバーがエクスポートする必要がある関数は非常に単純です。サーバー モジュールを要求するスクリプトはサーバーを起動するだけでよいからです。

サーバー スクリプトを start という関数に入れてからエクスポートします。

コードをコピー コードは次のとおりです。

var http = require("http") ;

function start() {
function onRequest(request, response) {
console.log("リクエストを受信しました。");
response.writeHead(200, {"Content-Type": " text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;


これで、サーバー コードはまだserver.js内にありますが、メイン ファイルindex.jsを作成し、その中でHTTPを開始できるようになります。

index.js ファイルを作成し、次の内容を書き込みます:

コードをコピーします コードは次のとおりです:

var サーバー = require("./server");

server.start();


ご覧のとおり、他の組み込みモジュールと同じようにサーバー モジュールを使用できます。このファイルをリクエストし、エクスポートされた変数を指定します。関数は次のとおりです。これで使用できるようになりました。

わかりました。これで、メイン スクリプトからアプリケーションを起動できるようになりました。これは以前と同じです:

コードをコピーします コードは次のとおりです。以下に続きます:

nodeindex.js

これで、アプリケーションのさまざまな部分をさまざまなファイルに配置し、モジュールを一緒に生成することでそれらを接続できるようになりました。

まだアプリケーション全体の最初の部分しかありません。HTTP リクエストを受信できます。しかし、何かをしなければなりません。サーバーは、異なる URL リクエストに対して異なる応答をする必要があります。

非常に単純なアプリケーションの場合、これをコールバック関数 onRequest() で直接実行できます。しかし、先ほども言ったように、この例をもう少し面白くするには、いくつかの抽象的な要素を追加する必要があります。

さまざまな HTTP リクエストの処理は、コード内の「ルーティング」と呼ばれる別の部分です。そこで、次にルーティングというモジュールを作成しましょう。

リクエストを「ルーティング」する方法

リクエストされた URL とその他の必須の GET パラメーターと POST パラメーターをルートに提供する必要があります。次に、ルートはこれらのデータに基づいて対応するコードを実行する必要があります (ここでの「コード」は、アプリケーション全体の 3 番目の部分に対応します)。一連のリクエストは実際に動作するハンドラーによって受信されます)。

したがって、HTTP リクエストを調べて、リクエストされた URL と GET/POST パラメータを抽出する必要があります。この関数がルーティングに属するかサーバーに属するか (またはモジュール自体の関数としても) は確かに議論の価値がありますが、ここでは暫定的に HTTP サーバーの関数として考慮されます。

必要なデータはすべてリクエスト オブジェクトに含まれており、onRequest() コールバック関数の最初のパラメータとして渡されます。ただし、このデータを解析するには、url モジュールとクエリ文字列モジュールである追加の Node.JS モジュールが必要です。

コードをコピーします コードは次のとおりです:

🎜> url.parse(string)。パス名 |

> http://localhost:8888/start?foo=bar&hello=world

クエリ文字列(文字列)["foo"] |

クエリ文字列(文字列) [「こんにちは」]

もちろん、querystring モジュールを使用して POST リクエスト本文のパラメータを解析することもできます。これについては後で説明します。

次に、onRequest() 関数にロジックを追加して、ブラウザーによって要求された URL パスを確認しましょう。

var http = require("http");
var url = require("url");

function start() {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("「 pathname 」のリクエストを受信しました.");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;
これで、アプリケーションはリクエストの URL パスによってさまざまなリクエストを区別できるようになりました。これにより、ルーティング (まだ完了していません) を使用してリクエストを URL パスにルーティングできるようになりました。ベースラインはハンドラーにマッピングされます。

私たちが構築しているアプリケーションでは、/start と /upload からのリクエストを別のコードで処理できることを意味します。これがどのように組み合わされるかは後で見ていきます。

ここで、router.js という名前のファイルを作成し、次の内容を追加します。

関数ルート(パス名) {
console.log("パス名に対するリクエストをルーティングしようとしています);
}

exports.route = Route;
ご覧のとおり、このコードは何も行いませんが、今のところはそのままになっています。ロジックを追加する前に、まずルーティングとサーバーを統合する方法を見てみましょう。

私たちのサーバーはルートの存在を認識し、それらを効果的に使用する必要があります。もちろん、この依存関係をサーバーにハードコードすることもできますが、他の言語でのプログラミングの経験から、これは面倒であることがわかっているため、依存関係の注入を使用してモジュールを緩やかに追加します (Martin Fowlers を参照してください)。背景知識として依存性注入に関する傑作)。

まず、サーバーの start() 関数を拡張して、ルーティング関数をパラメーターとして渡します。

コードをコピー コードは次のとおりです:

var http = require("http");
var url = require("url");

function start(route) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("「 pathname 」のリクエスト受信しました。");

ルート(パス名);

response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;


同時に、ルーティング関数をサーバーに挿入できるように、index.js を拡張します。
コードをコピーします コードは次のとおりです:

var server = require("./server");
var router = require ("./ルーター"); server.start(router.route);

ここで、渡した関数はまだ何も行いません。
ここでアプリケーション (nodeindex.js、このコマンドラインを常に覚えておいてください) を起動して URL をリクエストすると、アプリケーションが対応する情報を出力するのがわかります。これは、HTTP サーバーがすでにルーティングを使用していることを示します。


コードをコピーします コードは次のとおりです:
bash$nodeindex.js
/foo のリクエストを受信しました。
/foo のリクエストをルーティングしようとしています

(上記の出力では、/favicon.ico リクエストに関連する煩わしい部分が削除されています)。

行動主導の実行

また話題から逸れて、ここで関数型プログラミングについて話させてください。

関数を引数として渡すのは、技術的な理由だけではありません。ソフトウェア設計にとって、これは実際には哲学的な質問です。次のシナリオについて考えてみましょう。インデックス ファイルでルーター オブジェクトを渡すと、サーバーはこのオブジェクトのルート関数を呼び出すことができます。

このように、何かを渡し、サーバーはこれを使用して何かを完了します。こんにちは、ルーティングと呼ばれるものですが、これをルーティングするのを手伝ってくれませんか?

しかし、サーバーは実際にはそのようなものを必要としません。実際、物事を成し遂げるために必要なのは、物事ではなく、行動です。つまり、名詞は必要なく、動詞が必要です。

この概念における最も核心的で基本的なイデオロギーの変換を理解した後、私は自然に関数型プログラミングを理解しました。

私は Steve Yegge の傑作『Death Penalty in the Kingdom of Nouns』を読んだ後、関数型プログラミングを理解しました。君もこの本を読んだほうがいいよ、本当に。これは、私に読書の喜びを与えてくれたソフトウェアに関する本の 1 つです。

実際のリクエスト ハンドラーにルーティングされます

本題に戻りますが、これで、HTTP サーバーとリクエスト ルーティング モジュールは、一対の親しい兄弟のように、期待どおりに相互に通信できるようになりました。

もちろん、これだけでは十分ではありません。ルーティングとは、その名前が示すように、異なる URL を異なる方法で処理する必要があることを意味します。たとえば、/start を処理するための「ビジネス ロジック」は、/upload を処理するための「ビジネス ロジック」とは異なる必要があります。

現在の実装では、ルーティング プロセスはルーティング モジュールで「終了」し、ルーティング モジュールはリクエストに対して実際に「アクションを実行」するモジュールではありません。そうしないと、アプリケーションがより複雑になると、実行できなくなります。スケールがよくなります。

ルーティングターゲットとしての関数を一時的にリクエストハンドラーと呼びます。リクエスト ハンドラーの準備ができていない場合、ルーティング モジュールを改善してもほとんど意味がないため、今はルーティング モジュールの開発を急ぐのはやめましょう。

アプリケーションには新しいウィジェットが必要なので、新しいモジュールを追加します。それについて目新しい必要はもうありません。 requestHandlers というモジュールを作成し、リクエスト ハンドラーごとにプレースホルダー関数を追加して、これらの関数をモジュール メソッドとしてエクスポートしましょう:

Copy Code コードは次のとおりです。

function start() {
console.log("リクエスト ハンドラー 'start' が呼び出されました。");
}

function Upload() {
console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
}

exports.start = 開始;
exports.upload = アップロード;


このようにして、リクエスト ハンドラーとルーティング モジュールを接続して、ルーティングを「検索可能」にすることができます。

ここで決定を下さなければなりません: requestHandlers モジュールをルートにハードコーディングして使用するべきでしょうか、それとも少し依存関係の注入を追加すべきでしょうか?他のパターンと同様に、依存関係注入は用途のためだけに使用すべきではありませんが、この場合、依存関係注入を使用すると、ルートとリクエスト ハンドラーの間の結合が緩くなり、ルートがより再利用可能になります。

これは、リクエスト ハンドラーをサーバーからルートに渡す必要があることを意味しますが、これはさらにとんでもないことのように感じられます。メイン ファイルからサーバーまで大量のリクエスト ハンドラーを渡してから、それらを渡す必要があります。サーバーからルートまで。

それでは、これらのリクエスト ハンドラーをどのように渡すのでしょうか?実際のアプリケーションでは、ハンドラーが 2 つしかないので、このことは考えないでください。新しいリクエストが発生するたびに、リクエスト ハンドラーの数は増え続けます。 URL またはリクエスト ハンドラーをハンドラーにマッピングし、それを何度も投げます。さらに、ルーティングには if request == x then call ハンドラー y が多数あり、これもシステムを醜くしています。

よく考えてみてください。多くのものがあり、それぞれを文字列 (つまり、要求された URL) にマッピングする必要があります。連想配列は完全に機能しているようです。

しかし、結果は少し残念です。JavaScript は連想配列を提供しません -- 提供すると言えるでしょうか?実際、JavaScript では、そのような機能を実際に提供できるのはそのオブジェクトです。

これに関しては、http://msdn.microsoft.com/en-us/magazine/cc163419.aspx に優れた紹介文があります。ここに抜粋します。

C または C# では、オブジェクトについて話すときは、クラスまたは構造体のインスタンスを指します。オブジェクトには、インスタンス化されたテンプレート (いわゆるクラス) に応じて、さまざまなプロパティとメソッドがあります。しかし、JavaScript ではオブジェクトはこの概念ではありません。 JavaScript では、オブジェクトはキーと値のペアのコレクションです。JavaScript オブジェクトは文字列キーを含む辞書と考えることができます。

しかし、JavaScript オブジェクトが単なるキーと値のペアのコレクションである場合、どうやってメソッドを持つことができるでしょうか?ここでの値は文字列、数値、または...関数にすることができます。

さて、最後にコードに戻りましょう。オブジェクトを通じて一連のリクエスト ハンドラーを渡すことに決めたので、このオブジェクトを疎結合の方法で Route() 関数に挿入する必要があります。

最初にこのオブジェクトをメインファイルのindex.jsに導入します:

コードをコピーします コードは次のとおりです:

var サーバー = require("./server");
var router = require("./router");
var requestHandlers = require("./requestHandlers");

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers。アップロード;

server.start(router.route, handle);


handle は単なる「もの」(リクエスト ハンドラーのコレクション) ではありませんが、それでも、次のように動詞で名前を付けることをお勧めします。後で説明するように、ルーティングでより流暢な表現を使用できるようになります。

ご覧のとおり、異なる URL を同じリクエスト ハンドラーにマッピングするのは簡単です。requestHandlers.start に対応するキー「/」を持つプロパティをオブジェクトに追加するだけで、簡潔に構成された /start とリクエストのリクエストをクリーンアップできます。 / は開始ハンドラーによって処理されます。

オブジェクトの定義が完了したら、それを追加パラメーターとしてサーバーに渡します。この目的のために、server.js は次のように変更されます。

Copy code コードは次のとおりです:

var http = require("http");
var url = require("url");

function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for "パス名 " を受け取りました。");

ルート(ハンドル、パス名);

response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;


このようにして、start() 関数にハンドル パラメータを追加し、ハンドル オブジェクトを最初のパラメータとして Route() コールバック関数に渡します。

次に、route.js ファイル内の Route() 関数をそれに応じて変更します。

コードをコピーします コードは次のとおりです。以下:

function Route(handle, pathname) {
console.log("「 pathname のリクエストをルーティングしようとしています」);
if (typeof handle[pathname] === 'function') {
handle[パス名]();
} else {
console.log("パス名に対するリクエスト ハンドラーが見つかりませんでした);
}
}

exports.route = Route;


上記のコードを通じて、まず、指定されたパスに対応するリクエスト ハンドラーが存在するかどうかを確認し、存在する場合は、対応する関数を直接呼び出します。連想配列から要素を取得するのと同じように、渡されたオブジェクトからリクエスト処理関数を取得できるので、handle[pathname](); という形で簡潔かつスムーズな表現ができます。 「ねえ、この道を手伝ってください。」

これで、サーバー、ルート、リクエスト ハンドラーが一緒にできました。次に、アプリケーションを起動し、ブラウザで http://localhost:8888/start にアクセスします。次のログは、システムが正しいリクエスト ハンドラーを呼び出していることを示しています:

Copyコード コードは次のとおりです:

サーバーは開始しました。
/start の要求を受信しました。
/start の要求をルーティングしようとしています
リクエスト ハンドラー 'start' が呼び出されました。

ブラウザで http://localhost:8888/ を開くと、このリクエストが開始リクエスト ハンドラーによって処理されていることもわかります:
コードをコピーします コードは次のとおりです:

リクエスト / 受信しました。
リクエストをルーティングしようとしています/
リクエスト ハンドラー 'start' が呼び出されました。

リクエストハンドラーに応答させます

とても良いです。しかし、リクエスト ハンドラーが単なる「Hello World」ではなく、何らかの意味のある情報をブラウザーに返すことができれば便利です。

ここで覚えておくべきことは、ブラウザがリクエストを行った後に取得および表示される「Hello World」情報は、依然として、server.js ファイルの onRequest 関数から取得されるということです。

実際、「リクエストを処理する」とは単に「リクエストに応答する」ことを意味するため、onRequest 関数のようにリクエスト ハンドラーがブラウザと「対話」できるようにする必要があります。

不適切な実装

PHP や Ruby の技術的背景を持つ私たちのような開発者にとって、最も単純な実装方法は実際にはあまり信頼できません。効果的であるように見えますが、実際はそうではない可能性があります。

ここで言う「単純な実装」とは、リクエスト ハンドラーが onRequest 関数を通じてユーザーに表示したい情報を直接返す (return()) ことを意味します。

まずこのように実装してから、なぜこれが良い実装方法ではないのかを見てみましょう。

まず、リクエスト ハンドラーにブラウザーに表示する必要がある情報を返させることから始めましょう。 requestHandler.js を次の形式に変更する必要があります:

コードをコピーします コードは次のとおりです:

function start() {
console.log("リクエスト ハンドラー 'start' が呼び出されました。");
return "Hello Start";
}

function Upload() {
console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
return "Hello Upload";
}

exports.start = start;
exports.upload = Upload;


わかりました。同様に、リクエスト ルーティングは、リクエスト ハンドラーが返す情報をサーバーに返す必要があります。したがって、router.js を次の形式に変更する必要があります:
コードをコピーします コードは次のとおりです:

function Route(handle, pathname) {
console.log("「 pathname のリクエストをルーティングしようとしています」);
if (typeof handle[pathname] === 'function') {
return handle [pathname]();
} else {
console.log("No request handler found for " pathname);
return "404 Not found";
}
}

exports.route = Route;


上記のコードに示されているように、リクエストをルーティングできない場合は、関連するエラー情報も返します。

最後に、次のように、リクエスト ハンドラーによってリクエスト ルート経由で返されたコンテンツでブラウザに応答するように、server.js をリファクタリングする必要があります。

コードをコピー コードは次のとおりです:

var http = require("http");
var url = require("url");

function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for "パス名 " を受け取りました。");

response.writeHead(200, {"Content-Type": "text/plain"});
var content = Route(handle, pathname)
response.write(content);
応答.end();
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;


リファクタリングされたアプリケーションを実行すると、すべてが正常に動作します。 http://localhost:8888/start をリクエストすると、ブラウザは http をリクエストする "Hello Start" を出力します。 ://localhost:8888/upload は「Hello Upload」を出力し、http://localhost:8888/foo をリクエストすると「404 Not found」を出力します。

それで、何が問題なのでしょうか?簡単に言うと、将来リクエスト ハンドラーが非ブロッキング操作を実行する必要がある場合、アプリケーションは「ハング」します。

理解できない?それは問題ではありません。以下で詳しく説明しましょう。

ブロックと非ブロック

前に述べたように、リクエスト ハンドラーに非ブロッキング操作を含めると問題が発生します。ただし、これについて説明する前に、まずブロッキング操作とは何かを見てみましょう。

「ブロック」と「非ブロック」の具体的な意味については説明しません。ブロック操作がリクエスト ハンドラーに追加されたときに何が起こるかを見てみましょう。

ここでは、開始リクエスト ハンドラーを変更して、「Hello Start」を返す前に 10 秒待機させます。 JavaScript には sleep() のような操作がないため、実装をシミュレートするには少しの Hack を使用するだけです。

requestHandlers.js を次の形式に変更しましょう:

コードをコピーします コードは次のとおりです:

function start() {
console.log("リクエスト ハンドラー 'start' が呼び出されました。");

function sleep(milliSeconds) {
var startTime = new Date().getTime();
while (new Date().getTime() < startTime milliSeconds);
}

sleep(10000);
return "Hello Start";
}

function Upload() {
console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
return "Hello Upload";
}

exports.start = 開始;
exports.upload = アップロード;


上記のコードでは、関数 start() が呼び出されると、Node.js は 10 秒間待機してから「Hello Start」を返します。 Upload() が呼び出されると、前と同様にすぐに戻ります。

(もちろん、これは 10 秒間のスリープのシミュレーションにすぎません。実際のシナリオでは、長期的な計算操作など、このようなブロック操作が多数あります。)

変更によってどのような変化がもたらされたかを見てみましょう。

いつものように、最初にサーバーを再起動する必要があります。効果を確認するには、いくつかの比較的複雑な操作を実行する必要があります (これに従ってください): まず、2 つのブラウザ ウィンドウまたはタブを開きます。最初のブラウザ ウィンドウのアドレス バーに http://localhost:8888/start と入力します。ただし、まだ開かないでください。

2 番目のブラウザ ウィンドウのアドレス バーに「http://localhost:8888/upload」と入力します。まだ開かないでください。

次に、次の手順を実行します。最初のウィンドウ (「/start」) で Enter キーを押し、すぐに 2 番目のウィンドウ (「/upload」) に切り替えて Enter キーを押します。

何が起こったかに注目してください。/start URL のロードには 10 秒かかりましたが、これは予想どおりでした。ただし、/upload URL には実際には 10 秒かかり、対応するリクエスト ハンドラーには sleep() に似た操作がありませんでした。

これはなぜですか?その理由は、start() にブロック操作が含まれているためです。比喩的に言えば、「他のすべての処理作業がブロックされる」ということです。

Node は常に次のように自分自身を宣伝しているため、これは明らかに問題です。「Node では、コードを除いて、すべてが並行して実行されます。」

この文が意味するのは、Node.js はスレッドを追加しなくてもタスクを並列処理できる、つまり Node.js はシングルスレッドであるということです。イベント ループを介した並列操作が実装されているため、これを最大限に活用する必要があります。ブロック操作をできる限り避け、代わりにノンブロッキング操作を使用します。

ただし、ノンブロッキング操作を使用するには、処理に時間がかかる他の関数 (たとえば、10 秒間スリープする、データベースにクエリを実行する、または大規模なクエリを実行するなど) にパラメーターとして関数を渡すことによって、コールバックを使用する必要があります。計算回数)。

Node.js の場合、これは次のように処理されます。「ねえ、おそらく ExpensiveFunction() (翻訳者注: これは処理に時間がかかる関数を指します)、あなたは自分の処理を続けます、私 (Node.js スレッド)今のところはお待ちしません。引き続きコードの処理を行います。処理が完了したらコールバック関数を呼び出します。」

(イベント ポーリングについて詳しく知りたい場合は、Mixu のブログ投稿「node.js イベント ポーリングについて」を参照してください。)

次に、ノンブロッキング操作の間違った使い方を紹介します。

前回と同様に、問題を明らかにするためにアプリケーションを変更しました。

今回も開始リクエスト ハンドラーを使用して「操作」します。次の形式に変更します:


コードをコピーします コードは次のとおりです:
var exec = require( "child_process").exec;

function start() {

console.log("リクエスト ハンドラー 'start' が呼び出されました。");
var content = "empty";

exec("ls -lah", function (error, stdout, stderr) {

content = stdout;
});

コンテンツを返します;

}

function Upload() {

console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
return "Hello Upload";
}

exports.start = 開始;

exports.upload = アップロード;

上記のコードでは、新しい Node.js モジュール child_process を導入しました。これが使用される理由は、シンプルで実用的なノンブロッキング操作 exec() を実装するためです。

exec() は何をしますか? Node.jsからシェルコマンドを実行します。上記の例では、現在のディレクトリ (「ls -lah」) にあるすべてのファイルを取得し、/startURL が要求されたときにファイル情報をブラウザに出力するために使用しています。

上記のコードは非常に直感的です。新しい変数 content (初期値は「空」) を作成し、「ls -lah」コマンドを実行し、結果を content に割り当て、最後に content を返します。

いつものように、サーバーを起動し、「http://localhost:8888/start」にアクセスします。

美しい Web ページがコンテンツ「空」でロードされます。どうしたの?

この時点で、exec() がノンブロッキングにおいて魔法のような役割を果たすことはおおよそ推測できたかもしれません。これは実際には良いことであり、これを使用すると、アプリケーションを強制的に停止して操作を待機させることなく、非常に時間のかかるシェル操作を実行できます。

(これを証明したい場合は、「ls -lah」を「find /」などのより時間のかかる操作に置き換えることで効果が得られます)。

しかし、ブラウザに表示された結果から判断すると、ノンブロッキング操作には満足できませんね。

さて、次はこの問題を解決しましょう。その一方で、現在のアプローチが機能しない理由を見てみましょう。

問題は、非ブロッキング作業を実行するために、exec() がコールバック関数を使用することです。

この場合、コールバック関数は exec() の 2 番目のパラメーターとして渡される匿名関数です:

コードをコピー コードは次のとおりです。

function (error, stdout, stderr) {
content = stdout;
}

ここで問題の根本に迫ります。 : we コードは同期的に実行されます。つまり、exec() を呼び出した後、Node.js はすぐに戻りコンテンツを実行します。この時点では、exec() に渡されたコールバック関数がまだ実行されていないため、コンテンツはまだ「空」です。 . To - exec() の操作は非同期であるため。

ここでの「ls -lah」の操作は、実際には非常に高速です (現在のディレクトリに何百万ものファイルがある場合を除く)。これが、コールバック関数が迅速に実行される理由です。ただし、いずれにしても非同期であることに変わりはありません。

効果をより明確にするために、より時間のかかるコマンド「find /」を想像してみましょう。このコマンドは私のマシンで実行するのに約 1 分かかりますが、リクエスト ハンドラーでは「ls - lah」を入れています。 /start URL を開いたときに、「find /」に置き換えても、HTTP 応答をすぐに取得できます。明らかに、バックグラウンドで exec() が実行されると、Node.js 自体が次のコードを実行し続けます。ここでは、exec() に渡されるコールバック関数は、「find /」コマンドの実行後にのみ呼び出されると仮定します。

では、現在のディレクトリ内のファイルリストをユーザーに表示するにはどうすればよいでしょうか?

この悪い実装を理解したところで、リクエスト ハンドラーがブラウザのリクエストに正しい方法で応答する方法を紹介しましょう。

ノンブロッキング操作によるリクエストへの応答

先ほど「正しい方法」という言葉を言いました。実際、「正しい方法」は通常、単純なものではありません。

ただし、Node.js を使用した関数の受け渡しという実装ソリューションもあります。これを具体的に実装する方法を見てみましょう。

これまでのところ、アプリケーションはリクエスト ハンドラーによって返されたコンテンツ (リクエスト ハンドラーは最終的にユーザーにコンテンツを表示します) を HTTP サーバーに渡すことができました。

ここで、次の新しい実装方法を採用します。コンテンツをサーバーに渡す代わりに、今回はサーバーをコンテンツに「渡す」方法を使用します。 実際的な観点から見ると、応答オブジェクト (サーバーのコールバック関数 onRequest() から取得) は、要求ルーティングを通じて要求ハンドラーに渡されます。 ハンドラーは、そのオブジェクトの関数を使用してリクエストに応答できます。

これが原則です。このソリューションを段階的に実装してみましょう。

server.js から開始します:

コードをコピーします コードは次のとおりです:

var http = require("http");
var url = require("url");

function start(route, handle) {
function onRequest(request, response) {
var pathname = url.parse(request.url).pathname;
console.log("Request for "パス名 " を受け取りました。");

ルート(ハンドル、パス名、応答);
}

http.createServer(onRequest).listen(8888);
console.log("サーバーが起動しました。");
}

exports.start = start;


route() 関数から戻り値を取得する以前の方法と比較して、今回は応答オブジェクトを 3 番目のパラメーターとして Route() 関数に渡します。 onRequest() ハンドラー内の応答関連の関数呼び出しはすべて削除されます。これは、この部分の作業をroute() 関数で実行するためです。

router.js を見てみましょう:

コードをコピーします コードは次のとおりです:

function Route(ハンドル, パス名, 応答) {
console.log("「パス名」のリクエストをルーティングしようとしています);
if (typeof ハンドル[パス名] === '関数') {
handle[pathname](response);
} else {
console.log("No request handler found for " pathname);
response.writeHead(404, {"Content-Type" : "text /plain"});
response.write("404 Not found");
response.end();
}
}

exports.route = Route;


同じパターン: リクエスト ハンドラーから戻り値を取得する代わりに、今回は応答オブジェクトが直接渡されます。

対応するリクエスト ハンドラーがない場合は、直接「404」エラーを返します。

最後に、requestHandler.js を次の形式に変更します:

コードをコピーします コードは次のとおりです:

var exec = require("child_process").exec;

function start(response) {
console.log("リクエスト ハンドラー 'start' が呼び出されました。");

exec("ls -lah", function (error, stdout, stderr) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write (stdout);
response.end();
});
}

function Upload(response) {
console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Hello Upload");
response.end();
}

exports.start = start;
exports.upload = Upload;


ハンドラー関数は、リクエストに直接応答するために応答パラメーターを受け取る必要があります。

開始ハンドラーは exec() の匿名コールバック関数で要求応答操作を実行しますが、アップロード ハンドラーは単純に「Hello World」を応答しますが、今回は応答オブジェクトを使用します。

ここでアプリケーション (nodeindex.js) を再度起動すると、すべてが正常に動作します。

/start ハンドラーでの時間のかかる操作によって /upload リクエストへの即時応答がブロックされないことを証明したい場合は、requestHandlers.js を次の形式に変更できます:

コードをコピーします コードは次のとおりです:

var exec = require("child_process").exec;

function start(response) {
console.log("リクエスト ハンドラー 'start' が呼び出されました。");

exec("find /",
{ timeout: 10000, maxBuffer: 20000*1024 },
function (error, stdout, stderr) {
response.writeHead(200, {"Content- Type": "text/plain"});
response.write(stdout);
response.end();
});
}

function Upload(response) {
console.log("リクエスト ハンドラー 'upload' が呼び出されました。");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Hello Upload");
response.end();
}

exports.start = 開始;
exports.upload = アップロード;


In this way, when requesting http://localhost:8888/start, it will take 10 seconds to load, and when requesting http://localhost:8888/upload , it will respond immediately, even if the /start response is still being processed at this time.

More useful scenarios

So far, we have done well, however, our application has no practical use.

The server, request routing and request handler have been completed. Let's add interaction to the website according to the previous use case: the user selects a file, uploads the file, and then sees the uploaded file in the browser. To keep it simple, we assume that the user will only upload an image and our app will display that image into the browser.

Okay, let’s implement it step by step. Since we have already introduced a lot of JavaScript principles and technical content before, let’s speed up a little bit this time.

To implement this function, there are two steps as follows: First, let's take a look at how to handle POST requests (non-file uploads). After that, we use an external module of Node.js for file uploads. There are two reasons for this implementation.

First, although handling basic POST requests in Node.js is relatively simple, you can still learn a lot in the process.
Second, using Node.js to handle file uploads (multipart POST requests) is relatively complex and is outside the scope of this book. However, how to use external modules is within the scope of this book.

Handling POST requests

Consider a simple example: we display a textarea for the user to enter content, and then submit it to the server via a POST request. Finally, the server receives the request and displays the input content to the browser through the handler.

The

/start request handler is used to generate a form with a text area, so we modify requestHandlers.js to the following form:

function start(response) {
console.log("Request handler 'start' was called.");

var body = ''
''
''
''
''
'

'
''
' '
'
'
''
'';

response.writeHead(200, {"Content-Type": "text/html"});
response.write(body);
response.end();
}

function upload(response) {
console.log("Request handler 'upload' was called.");
response.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Hello Upload");
response.end();
}

exports.start = start;
exports.upload = upload;
Okay, now our application is very complete and can even win Webby Awards, haha. (Translator's Note: The Webby Awards are an award sponsored by the International Academy of Digital Arts and Sciences to select the best websites in the world. For details, please see the detailed description) You can see it by visiting http://localhost:8888/start in your browser It’s a simple form, remember to restart the server!

You might say: This way of placing visual elements directly in the request handler is too ugly. That's true, but I don't want to introduce patterns such as MVC in this book, because it is not relevant to your understanding of JavaScript or Node.js environments.

In the remaining space, we will discuss a more interesting issue: when the user submits the form, the /upload request handler is triggered to handle the POST request.

Now that we are experts among novices, it is natural to think of using asynchronous callbacks to process POST request data non-blockingly.

Non-blocking is used here

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。