Maison > interface Web > js tutoriel > Mini-livre de didacticiel d'introduction à Node.js, exemple complet de développement d'applications Web d'introduction à Node.js_Connaissances de base

Mini-livre de didacticiel d'introduction à Node.js, exemple complet de développement d'applications Web d'introduction à Node.js_Connaissances de base

WBOY
Libérer: 2016-05-16 16:53:09
original
927 Les gens l'ont consulté

本书状态

你正在阅读的已经是本书的最终版。因此,只有当进行错误更正以及针对新版本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程序员。而我并非其中一员。

Je suis la personne décrite dans la section précédente. J'étais familier avec le développement d'applications Web backend, mais j'étais nouveau dans le « vrai » JavaScript et Node.js. Je n'ai appris que récemment certains concepts avancés de JavaScript et je n'ai aucune expérience pratique.

Ce livre n'est donc pas un livre "de l'entrée au maître", mais plutôt un livre "du débutant à l'entrée avancée".

En cas de succès, alors ce livre sera le tutoriel que je voulais le plus avoir lorsque j'ai commencé à apprendre Node.js.

JavaScript côté serveur

JavaScript s'est d'abord exécuté dans le navigateur. Cependant, le navigateur ne fournissait qu'un contexte, qui définissait ce qui pouvait être fait en utilisant JavaScript, mais ne « disait » pas grand-chose sur ce que le langage JavaScript lui-même pouvait faire. En fait, JavaScript est un langage « complet » : il peut être utilisé dans différents contextes et ses capacités sont aussi bonnes que celles d’autres langages similaires.

Node.js est en fait un autre contexte qui permet d'exécuter du code JavaScript sur le backend (hors de l'environnement du navigateur).

Pour exécuter du code JavaScript en arrière-plan, le code doit d'abord être interprété puis exécuté correctement. C'est le principe de Node.js. Il utilise la machine virtuelle V8 de Google (l'environnement d'exécution JavaScript utilisé par le navigateur Chrome de Google) pour interpréter et exécuter le code JavaScript.

De plus, Node.js est livré avec de nombreux modules utiles qui peuvent simplifier de nombreuses tâches répétitives, telles que la sortie de chaînes vers le terminal.

Par conséquent, Node.js est en fait à la fois un environnement d'exécution et une bibliothèque.

Pour utiliser Node.js, vous devez d'abord l'installer. Concernant la façon d’installer Node.js, je n’entrerai pas dans les détails ici. Vous pouvez directement vous référer au guide d’installation officiel. Une fois l'installation terminée, continuez à revenir et à lire le reste de ce livre.

« Bonjour tout le monde »

D'accord, fini les "bêtises", commençons notre première application Node.js : "Hello World".

Ouvrez votre éditeur préféré et créez un fichier helloworld.js. Ce que nous devons faire est de sortir "Hello World" sur STDOUT. Voici le code pour implémenter cette fonction :

.

Copier le code Le code est le suivant :
console.log("Hello World");

Enregistrez le fichier et exécutez-le via Node.js :

Copier le code Le code est le suivant :
node helloworld.js

Si c'est normal, Hello World sera affiché dans le terminal.

D'accord, j'avoue que cette application est un peu ennuyeuse, alors passons aux « trucs secs ».

Une application web complète basée sur Node.js

Cas d'utilisation

Rendons l'objectif simple, mais suffisamment réaliste :

1. Les utilisateurs peuvent utiliser notre application via le navigateur.
2. Lorsque l'utilisateur demande http://domain/start, il peut voir une page d'accueil avec un formulaire de téléchargement de fichier.
3. L'utilisateur peut sélectionner une image et soumettre le formulaire, puis le fichier sera téléchargé sur http://domain/upload Une fois le téléchargement terminé, l'image sera affichée sur la page.
C'est tout. Vous pouvez également aller sur Google maintenant et trouver quelque chose à manipuler pour compléter la fonction. Mais ne faisons pas ça maintenant.

De plus, pour atteindre cet objectif, nous avons besoin de plus qu'un simple code de base, que le code soit élégant ou non. Nous devons également faire abstraction de cela pour trouver un moyen de créer des applications Node.js plus complexes.

Appliquer différents modules pour l'analyse

Décomposons cette application. Afin de mettre en œuvre les cas d'utilisation ci-dessus, quelles parties devons-nous implémenter ?

1. Nous devons fournir des pages Web, nous avons donc besoin d'un serveur HTTP
2. Pour différentes requêtes, notre serveur doit donner des réponses différentes en fonction de l'URL demandée, nous avons donc besoin d'un itinéraire pour acheminer les requêtes. au gestionnaire de requêtes
3. Une fois la requête reçue par le serveur et transmise via la route, elle doit être traitée, nous avons donc besoin du gestionnaire de requêtes final
4. POSTez les données, et encapsulez les données dans un format plus convivial et transmettez-les au programme de traitement des demandes, vous devez donc demander la fonction de traitement des données
5. Nous devons non seulement traiter la demande correspondant à l'URL, mais également afficher le contenu, ce qui signifie que nous avons besoin d'une logique d'affichage pour que le gestionnaire de requêtes envoie le contenu au navigateur de l'utilisateur
6. Enfin, l'utilisateur doit télécharger des images, nous avons donc besoin de la fonction de gestion du téléchargement pour gérer les détails de cela
Commençons par réfléchir à la façon dont nous construirions cette structure en utilisant PHP. Généralement nous utiliserons un serveur HTTP Apache avec le module mod_php5.
De ce point de vue, l'ensemble de l'exigence « réception d'une requête HTTP et diffusion d'une page Web » n'a pas du tout besoin d'être gérée par PHP.

Mais pour Node.js, le concept est complètement différent. Lorsque nous utilisons Node.js, nous implémentons non seulement une application, mais également un serveur HTTP complet. En fait, nos applications Web et les serveurs Web correspondants sont fondamentalement les mêmes.

Cela ressemble à beaucoup de travail à faire, mais ensuite nous réaliserons progressivement que ce n'est pas une chose gênante pour Node.js.

Commençons maintenant le chemin de la mise en œuvre, en commençant par le premier serveur partiellement HTTP.

Modules pour la création d'applications

Un serveur HTTP basique

Quand j'étais sur le point de commencer à écrire ma première "vraie" application Node.js, non seulement je ne savais pas comment écrire du code Node.js, mais je ne savais pas non plus comment l'organiser.
Dois-je tout mettre dans un seul fichier ? Il existe de nombreux tutoriels sur Internet qui vous apprendront à mettre toute la logique dans un serveur HTTP de base écrit en Node.js. Mais que se passe-t-il si je souhaite ajouter plus de contenu tout en gardant le code lisible ?

En fait, il est assez simple de conserver la séparation des codes tant que vous placez le code des différentes fonctions dans différents modules.

Cette approche vous permet d'avoir un fichier principal propre que vous pouvez exécuter avec Node.js et vous pouvez avoir des modules propres qui peuvent être appelés par le fichier principal et d'autres modules ;

Alors, créons maintenant un fichier principal qui démarre notre application et un module qui contient le code de notre serveur HTTP.

Dans mon esprit, appeler le fichier principal index.js est plus ou moins un format standard. Placer le module serveur dans un fichier appelé server.js est facile à comprendre.

Commençons par le module serveur. Créez un fichier appelé server.js dans le répertoire racine de votre projet et écrivez le code suivant :

Copier le code Code comme suit :

var http = require("http");

http.createServer(function(request, Response) {
Response.writeHead(200, {"Content-Type": "text/plain"});
Response.write("Hello World") ;
réponse.end();
}).listen(8888);


C'est fait ! Vous venez de terminer un serveur HTTP fonctionnel. Pour le prouver, exécutons et testons ce code. Tout d’abord, exécutez votre script avec Node.js :

node server.js
Ensuite, ouvrez le navigateur et visitez http://localhost:8888/, vous verrez une page Web avec "Hello World" écrit dessus.

C’est intéressant, n’est-ce pas ? Parlons d’abord du serveur HTTP et laissons de côté comment organiser le projet. Qu’en pensez-vous ? Je promets que nous découvrirons cela plus tard.

Analyser le serveur HTTP

Alors ensuite, analysons la composition de ce serveur HTTP.

La première ligne demande le module http fourni avec Node.js et l'assigne à la variable http.

Ensuite, nous appelons la fonction fournie par le module http : createServer. Cette fonction renverra un objet. Cet objet a une méthode appelée Listen. Cette méthode a un paramètre numérique qui spécifie le numéro de port sur lequel le serveur HTTP écoute.

Ignorons pour l’instant la définition de la fonction entre parenthèses de http.createServer.

On aurait pu utiliser un code comme celui-ci pour démarrer le serveur et écouter sur le port 8888 :

Copiez le code Le code est comme suit :

var http = require("http");

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


Ce code démarrera uniquement un serveur en écoute sur le port 8888. Il ne fait rien d'autre. la demande recevra une réponse.

La partie la plus intéressante (et, si vous êtes habitué à un langage plus conservateur comme PHP, bizarre) est le premier argument de createSever(), une définition de fonction.

En fait, cette définition de fonction est le premier et unique paramètre de createServer(). Parce qu'en JavaScript, les fonctions peuvent être transmises comme d'autres variables.

Effectuer un transfert de fonction

Par exemple, vous pouvez faire ceci :

Copiez le code Le code est le suivant :

function say(word) {
console.log(word);
}

fonction exécuter(uneFonction, valeur) {
uneFonction(valeur);
}

execute(say, "Bonjour");
Veuillez lire attentivement ce code ! Ici, nous passons la fonction say comme première variable de la fonction d'exécution. Ce qui est renvoyé ici n'est pas la valeur de retour de say, mais say lui-même !

De cette façon, say devient la variable locale someFunction en exécution. Execute peut utiliser la fonction say en appelant someFunction() (avec parenthèses).

Bien sûr, puisque say a une variable, perform peut transmettre une telle variable lors de l'appel de someFunction.

On peut, comme je viens de le faire, passer une fonction comme variable par son nom. Mais on n'est pas obligé de faire le tour de ce cercle « définir d'abord, puis passer » On peut directement définir et passer cette fonction entre parenthèses d'une autre fonction :

Copier. code Le code est le suivant :

fonction exécuter(uneFonction, valeur) {
uneFonction(valeur);
}

execute(function(word){ console.log(word) }, "Bonjour");
Nous définissons directement la fonction que nous allons passer à exécuter où exécuter accepte le premier paramètre.

De cette façon, nous n’avons même pas besoin de donner un nom à la fonction, c’est pourquoi on l’appelle fonction anonyme.

C'est notre première rencontre rapprochée avec ce que je considère comme du JavaScript « avancé », mais nous devons encore procéder étape par étape. Pour l'instant, acceptons ceci : en JavaScript, une fonction peut recevoir un paramètre comme une autre fonction. Nous pouvons d'abord définir une fonction, puis la transmettre, ou nous pouvons définir la fonction directement là où les paramètres sont transmis.

Comment le passage de fonctions fait fonctionner les serveurs HTTP

Avec cette connaissance, jetons un coup d'œil à notre serveur HTTP simple mais pas simple :

Copier le code Le code est comme suit :

var http = require("http");

http.createServer(function(request, Response) {
Response.writeHead(200, {"Content-Type": "text/plain"});
Response.write("Hello World") ;
réponse.end();
}).listen(8888);


Maintenant, cela devrait paraître beaucoup plus clair : nous avons passé une fonction anonyme à la fonction createServer.

Le même objectif peut être atteint avec un code comme celui-ci :

Copiez le code Le code est le suivant :

var http = require("http");

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

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


Peut-être que maintenant nous devrions poser cette question : Pourquoi utilisons-nous cette méthode ?

Rappels basés sur des événements

Cette question n'est pas facile à répondre (du moins pour moi), mais c'est la manière native dont Node.js fonctionne. C'est basé sur des événements, c'est pourquoi c'est si rapide.

Vous voudrez peut-être prendre un moment pour lire le chef-d'œuvre de Felix Geisendörfer, Understanding node.js, qui fournit quelques connaissances de base.

Tout se résume au fait que "Node.js est piloté par les événements". Eh bien, en fait, je ne sais pas exactement ce que signifie cette phrase. Mais je vais essayer d'expliquer pourquoi il est logique pour nous d'écrire des applications Web avec Node.js.

Lorsque nous utilisons la méthode http.createServer, bien sûr, nous ne voulons pas seulement qu'un serveur écoute sur un certain port, nous voulons également qu'il fasse quelque chose lorsque le serveur reçoit une requête HTTP.

Le problème est que c'est asynchrone : les requêtes peuvent arriver à tout moment, mais notre serveur fonctionne en un seul processus.

Lors de l'écriture d'applications PHP, nous ne nous en soucions pas du tout : chaque fois qu'une requête arrive, le serveur Web (généralement Apache) crée un nouveau processus pour cette requête et commence à exécuter les processus correspondants du début à la fin. Script PHP.

Donc, dans notre programme Node.js, lorsqu'une nouvelle requête arrive sur le port 8888, comment contrôlons-nous le processus ?

Eh bien, c'est là que la conception événementielle de Node.js/JavaScript peut vraiment aider - même si nous devons encore apprendre de nouveaux concepts pour la maîtriser. Voyons comment ces concepts s'appliquent à notre code serveur.

Nous avons créé le serveur et passé une fonction à la méthode qui l'a créé. Chaque fois que notre serveur reçoit une requête, cette fonction est appelée.

Nous ne savons pas quand cela se produira, mais nous avons maintenant un endroit pour gérer la requête : c'est la fonction à laquelle nous l'avons transmise. Peu importe qu'il s'agisse d'une fonction prédéfinie ou d'une fonction anonyme.

C'est le rappel légendaire. Nous passons une fonction à une méthode, et cette méthode appelle cette fonction pour effectuer un rappel lorsqu'un événement correspondant se produit.

Pour moi au moins, il a fallu du travail pour le comprendre. Si vous n'êtes toujours pas sûr, lisez le blog de Felix.

Reprenons ce nouveau concept. Comment prouver qu'après la création du serveur, notre code continue d'être valide même si aucune requête HTTP n'arrive et que notre fonction de rappel n'est pas appelée ? Essayons ceci :

Copiez le code Le code est le suivant :

var http = require ("http ");

function onRequest(request, réponse) {
console.log("Demande reçue.");
réponse.writeHead(200, {"Content-Type": "text/plain"});
réponse.write("Hello World");
réponse.end();
}

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

console.log("Le serveur a démarré.");


Remarque : lorsque onRequest (notre fonction de rappel) est déclenché, j'utilise console.log pour afficher un morceau de texte. Une fois que le serveur HTTP commence à fonctionner, il génère également un morceau de texte.

Lorsque nous exécutons node server.js comme d'habitude, il affichera immédiatement "Le serveur a démarré sur la ligne de commande". Lorsque nous faisons une requête au serveur (visitez http://localhost:8888/ dans le navigateur), le message « Demande reçue » apparaîtra sur la ligne de commande.

Il s'agit de JavaScript côté serveur asynchrone piloté par les événements et de ses rappels !

(Veuillez noter que lorsque nous accédons à la page Web sur le serveur, notre serveur peut afficher "Demande reçue." deux fois. En effet, la plupart des serveurs essaieront de lire lorsque vous accéderez à http://localhost:8888/ Get http ://localhost:8888/favicon.ico )

Comment le serveur gère les requêtes

D'accord, analysons brièvement la partie restante de notre code serveur, qui est la partie principale de notre fonction de rappel onRequest().

Lorsque le rappel démarre et que notre fonction onRequest() est déclenchée, deux paramètres sont transmis : la demande et la réponse.

Ce sont des objets dont vous pouvez utiliser les méthodes pour gérer les détails d'une requête HTTP et répondre à la requête (comme renvoyer quelque chose au navigateur demandeur).

Notre code est donc le suivant : lors de la réception d'une requête, utilisez la fonction response.writeHead() pour envoyer un statut HTTP 200 et le type de contenu de l'en-tête HTTP (content-type), et utilisez la réponse.write() fonction pour ajouter le corps HTTP correspondant Envoyez le texte "Hello World".

Enfin, nous appelons réponse.end() pour compléter la réponse.

Pour l'instant, nous ne nous soucions pas des détails de la requête, nous n'utilisons donc pas l'objet de requête.

Où placer le module serveur

OK, comme je l'ai promis, nous pouvons maintenant revenir à la façon dont nous organisons nos candidatures. Nous avons maintenant un code de serveur HTTP très basique dans le fichier server.js, et j'ai mentionné que nous avons généralement un fichier appelé index.js qui appelle d'autres modules de l'application (comme le module serveur HTTP dans server.js Boot). et lancez l'application.

Parlons maintenant de comment transformer server.js en un véritable module Node.js afin qu'il puisse être utilisé par notre fichier principal index.js (pas encore démarré).

Peut-être avez-vous remarqué que nous avons utilisé des modules dans le code. Comme ceci :

Copier le code Le code est le suivant :

var http = require(" http") ;

...

http.createServer(...);


Node.js est livré avec un module appelé "http", nous le demandons dans notre code et attribuons la valeur de retour à une variable locale.

Cela transforme notre variable locale en un objet avec toutes les méthodes publiques fournies par le module http.

C'est une convention de donner à ces variables locales le même nom que le nom du module, mais vous pouvez également l'utiliser à votre guise :

Copier le code Le code est le suivant :

var foo = require("http");

...

foo.createServer(...);


Très bien, on sait déjà comment utiliser les modules internes de Node.js. Comment créer notre propre module et comment l'utiliser ?

Vous le comprendrez lorsque nous transformerons server.js en un véritable module.

En fait, nous n’avons pas besoin de faire trop de changements. Transformer un morceau de code en module signifie que nous devons exporter la partie de ses fonctionnalités que nous souhaitons fournir au script qui demande le module.

Actuellement, les fonctions que notre serveur HTTP doit exporter sont très simples, car le script qui demande le module serveur n'a besoin que de démarrer le serveur.

Nous mettons notre script serveur dans une fonction appelée start, que nous exporterons ensuite.

Copier le code Le code est le suivant :

var http = require("http") ;

function start() {
function onRequest(request, réponse) {
console.log("Demande reçue.");
réponse.writeHead(200, {"Content-Type": " text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;


Avec cela, nous pouvons maintenant créer notre fichier principal index.js et y démarrer notre HTTP, bien que le code du serveur soit toujours dans server.js.

Créez le fichier index.js et écrivez le contenu suivant :

Copiez le code Le code est le suivant :

var serveur = require("./server");

server.start();


Comme vous pouvez le voir, nous pouvons utiliser le module serveur comme n'importe quel autre module intégré : demandez ce fichier et pointez-le vers une variable avec l'exporté. La fonction est maintenant prêt à être utilisé.

D'accord. Nous pouvons maintenant lancer notre application depuis notre script principal, qui est toujours le même qu'avant :

Copiez le code Le code est tel suit :

node index.js

Super, nous pouvons maintenant mettre différentes parties de notre application dans différents fichiers et les connecter en générant des modules ensemble.

Nous n'avons encore que la partie initiale de l'ensemble de l'application : nous pouvons recevoir des requêtes HTTP. Mais nous devons faire quelque chose : le serveur doit répondre différemment aux différentes requêtes URL.

Pour une application très simple, vous pouvez faire cela directement dans la fonction de rappel onRequest(). Mais comme je l'ai dit, nous devrions ajouter quelques éléments abstraits pour rendre notre exemple un peu plus intéressant.

La gestion des différentes requêtes HTTP est une partie différente de notre code, appelée "routage" - créons donc ensuite un module appelé routage.

Comment "acheminer" les requêtes

Nous devons fournir l'URL demandée et les autres paramètres GET et POST requis pour la route. Ensuite, la route doit exécuter le code correspondant en fonction de ces données (le "code" correspond ici à la troisième partie de l'application entière : une série de requêtes reçues par le gestionnaire qui fonctionne réellement).

Par conséquent, nous devons examiner la requête HTTP et extraire l'URL demandée et les paramètres GET/POST. La question de savoir si cette fonction appartient au routage ou au serveur (ou même en tant que fonction du module lui-même) mérite effectivement d'être discutée, mais ici elle est provisoirement considérée comme la fonction de notre serveur HTTP.

Toutes les données dont nous avons besoin seront contenues dans l'objet request, qui est passé comme premier paramètre de la fonction de rappel onRequest(). Mais pour analyser ces données, nous avons besoin de modules Node.JS supplémentaires, qui sont les modules url et querystring.

Copier le code Le code est le suivant :

🎜> url.parse(string). pathname |

> http://localhost:8888/start?foo=bar&hello=world
                                                                                                     
querystring(string)["foo"] |

querystring(string) ["bonjour"]

Bien sûr, nous pouvons également utiliser le module querystring pour analyser les paramètres dans le corps de la requête POST, ce qui sera démontré plus tard.

Ajoutons maintenant un peu de logique à la fonction onRequest() pour connaître le chemin URL demandé par le navigateur :

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

function start() {
function onRequest(request, réponse) {
var pathname = url.parse(request.url).pathname;
console.log("Demande de " pathname " reçue .");
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello World");
response.end();
}

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;
D'accord, notre application peut désormais distinguer les différentes requêtes par le chemin URL de la requête - cela nous permet d'utiliser le routage (pas encore terminé) pour acheminer la requête vers le chemin URL. Les lignes de base sont mappées sur les gestionnaires.

Dans l'application que nous construisons, cela signifie que les requêtes de /start et /upload peuvent être traitées avec un code différent. Nous verrons plus tard comment cela s’articule.

Nous pouvons maintenant écrire la route. Créez un fichier nommé router.js et ajoutez le contenu suivant :

function route(pathname) {
console.log("À propos d'acheminer une requête pour "pathname);
}

exports.route = route;
Comme vous pouvez le constater, ce code ne fait rien, mais pour l'instant il est comme il se doit. Avant d'ajouter plus de logique, voyons d'abord comment intégrer le routage et les serveurs.

Nos serveurs doivent être conscients de l'existence des routes et les utiliser efficacement. Nous pourrions bien sûr coder en dur cette dépendance sur le serveur, mais l'expérience de la programmation dans d'autres langages nous dit que ce serait pénible, nous utiliserons donc l'injection de dépendances pour ajouter vaguement la route des modules (vous pouvez lire Martin Fowlers. 'chef-d'œuvre sur l'injection de dépendances pour les connaissances de base).

Tout d'abord, étendons la fonction start() du serveur pour passer la fonction de routage en paramètre :

Copier le code Le code est la suivante :

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

function start(route) {
function onRequest(request, réponse) {
var pathname = url.parse(request.url).pathname;
console.log("Demande de " pathname " reçu.");

route(chemin);

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

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;


En même temps, nous étendrons index.js en conséquence afin que la fonction de routage puisse être injectée dans le serveur :
Copiez le code Le code est le suivant :

var server = require("./server");
var router = require ("./routeur");

server.start(router.route);


Ici, la fonction que nous passons ne fait toujours rien.

Si vous démarrez l'application maintenant (node ​​​​index.js, souvenez-vous toujours de cette ligne de commande) puis demandez une URL, vous verrez l'application afficher les informations correspondantes, ce qui indique que notre serveur HTTP utilise déjà le routage module, et Le chemin demandé sera transmis à l'itinéraire :

Copiez le code Le code est le suivant :

bash$ node index.js
Demande pour /foo reçue.
Sur le point d'acheminer une demande pour /foo

(La sortie ci-dessus a supprimé les parties gênantes liées à la requête /favicon.ico).

Exécution basée sur le comportement

S'il vous plaît, permettez-moi de m'écarter à nouveau du sujet et de parler ici de programmation fonctionnelle.

Passer des fonctions comme arguments ne se fait pas uniquement pour des raisons techniques. Pour la conception de logiciels, il s’agit en réalité d’une question philosophique. Pensez à ce scénario : dans le fichier d'index, nous pouvons transmettre l'objet routeur, et le serveur peut alors appeler la fonction de route de cet objet.

Juste comme ça, nous transmettons quelque chose, puis le serveur utilise cette chose pour terminer quelque chose. Salut, ce truc appelé routage, pouvez-vous m'aider à acheminer ça ?

Mais le serveur n’a pas vraiment besoin d’une telle chose. Il suffit de faire avancer les choses. En fait, pour faire avancer les choses, vous n'avez pas besoin de choses du tout, vous avez besoin d'actions. Autrement dit, vous n’avez pas besoin d’un nom, vous avez besoin d’un verbe.

Après avoir compris la transformation idéologique la plus fondamentale et la plus fondamentale de ce concept, j'ai naturellement compris la programmation fonctionnelle.

J'ai compris la programmation fonctionnelle après avoir lu le chef-d'œuvre de Steve Yegge, La peine de mort dans le royaume des noms. Vous devriez vraiment lire ce livre aussi. C'est l'un des livres sur les logiciels qui m'a jamais donné le plaisir de lire.

Acheminé vers le véritable gestionnaire de requêtes

Retour au sujet, désormais notre serveur HTTP et notre module de routage de requêtes peuvent communiquer entre eux comme nous l'espérions, comme deux frères proches.

Bien sûr, cela ne suffit pas. Le routage, comme son nom l'indique, signifie que nous devons gérer différentes URL de différentes manières. Par exemple, la « logique métier » pour le traitement /start doit être différente de celle pour le traitement /upload.

Dans l'implémentation actuelle, le processus de routage "se termine" dans le module de routage, et le module de routage n'est pas le module qui "agit réellement" sur la requête, sinon lorsque notre application deviendra plus complexe, elle ne pourra pas À la balance bien.

Nous appelons temporairement la fonction la cible de routage en tant que gestionnaire de requêtes. Ne nous précipitons pas dans le développement du module de routage maintenant, car il ne sert à rien d'améliorer le module de routage si le gestionnaire de requêtes n'est pas prêt.

Les applications nécessitent de nouveaux widgets, alors ajoutez de nouveaux modules : vous n'avez plus besoin de vous sentir sophistiqué. Créons un module appelé requestHandlers, et pour chaque gestionnaire de requêtes, ajoutons une fonction d'espace réservé, puis exportons ces fonctions en tant que méthodes de module :

Copier le code Le code est le suivant :

function start() {
console.log("Le gestionnaire de requêtes 'start' a été appelé.");
}

function upload() {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
}

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


De cette façon, nous pouvons connecter le gestionnaire de requêtes et le module de routage pour rendre le routage "trouvable".

Ici, nous devons prendre une décision : devons-nous coder en dur le module requestHandlers dans la route à utiliser, ou ajouter une petite injection de dépendances ? Bien que, comme d'autres modèles, l'injection de dépendances ne doive pas être utilisée uniquement à des fins d'utilisation, dans ce cas, l'utilisation de l'injection de dépendances peut rendre le couplage entre la route et le gestionnaire de requêtes plus lâche, et donc rendre la route plus réutilisable.

Cela signifie que nous devons transmettre les gestionnaires de requêtes du serveur à la route, mais cela semble encore plus scandaleux. Nous devons transmettre un tas de gestionnaires de requêtes depuis notre fichier principal vers le serveur, puis les transmettre avec Passé. du serveur à la route.

Alors, comment pouvons-nous transmettre ces gestionnaires de requêtes ? Ne regardez pas le fait que nous n'avons plus que 2 gestionnaires maintenant. Dans une application réelle, le nombre de gestionnaires de requêtes continuera d'augmenter. Nous ne voulons certainement pas avoir à terminer la requête dans la route à chaque fois. nouvelle URL ou gestionnaire de requêtes Mappage avec les gestionnaires et lancement encore et encore. De plus, il y a beaucoup de if request == x then call handler y dans le routage, ce qui rend également le système laid.

Réfléchissez bien, il y a beaucoup de choses, dont chacune doit être mappée à une chaîne (c'est-à-dire l'URL demandée) ? Il semble qu'un tableau associatif fonctionne parfaitement.

Mais le résultat est un peu décevant, JavaScript ne fournit pas de tableaux associatifs -- pouvez-vous dire que oui ? En fait, en JavaScript, ce sont ses objets qui peuvent réellement fournir de telles fonctionnalités.

À cet égard, http://msdn.microsoft.com/en-us/magazine/cc163419.aspx a une bonne introduction, j'en vais un extrait ici :

En C ou C#, quand on parle d'objets, on fait référence à des instances de classes ou de structures. Les objets ont des propriétés et des méthodes différentes selon le modèle (appelé classe) à partir duquel ils sont instanciés. Mais les objets ne correspondent pas à ce concept en JavaScript. En JavaScript, un objet est une collection de paires clé/valeur : vous pouvez considérer un objet JavaScript comme un dictionnaire avec des clés de chaîne.

Mais si un objet JavaScript n’est qu’une collection de paires clé/valeur, comment peut-il avoir des méthodes ? Eh bien, la valeur ici peut être une chaîne, un nombre ou... une fonction !

Bon, revenons enfin au code. Maintenant que nous avons décidé de faire passer une série de gestionnaires de requêtes via un objet, nous devons injecter cet objet dans la fonction route() de manière faiblement couplée.

On introduit d'abord cet objet dans le fichier principal index.js :

Copiez le code Le code est le suivant :

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

var handle = {}
handle["/"] = requestHandlers.start;
handle["/start"] = requestHandlers.start;
handle["/upload"] = requestHandlers. télécharger ;

server.start(router.route, handle);


Bien que handle ne soit pas seulement une "chose" (une collection de gestionnaires de requêtes), je recommande quand même de le nommer avec un verbe, comme ceci nous permet d'utiliser des expressions plus fluides dans le routage, comme expliqué plus loin.

Comme vous pouvez le constater, mapper différentes URL sur le même gestionnaire de requêtes est simple : il suffit d'ajouter une propriété avec la clé "/" à l'objet, correspondant à requestHandlers.start, et nous pouvons nettoyer les requêtes pour une configuration simple de /start et / sont gérés par le gestionnaire de démarrage.

Après avoir terminé la définition de l'objet, nous le transmettons au serveur comme paramètre supplémentaire. A cet effet, server.js est modifié comme suit :

Copier. code Le code est le suivant :

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

function start(route, handle) {
function onRequest(request, réponse) {
var pathname = url.parse(request.url).pathname;
console.log("Demande de " chemin " reçu. ");

route(handle, chemin);

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

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;


De cette façon, nous ajoutons le paramètre handle dans la fonction start() et passons l'objet handle comme premier paramètre à la fonction de rappel route().

Ensuite nous modifions la fonction route() dans le fichier route.js en conséquence :

Copiez le code Le code est tel suit :

function route(handle, pathname) {
console.log("À propos d'acheminer une requête pour "pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname]();
} else {
console.log("Aucun gestionnaire de requêtes trouvé pour " pathname);
}
}

exports.route = route;


Grâce au code ci-dessus, nous vérifions d'abord si le gestionnaire de requêtes correspondant au chemin donné existe, et s'il existe, appelons directement la fonction correspondante. Nous pouvons obtenir la fonction de traitement des requêtes de l'objet passé de la même manière que nous obtenons des éléments du tableau associatif, nous avons donc une expression concise et fluide sous la forme de handle[pathname]();, qui donne l'impression d'être mentionné au début à : "Hé, s'il te plaît, aide-moi avec ce chemin".

Avec cela, nous avons le serveur, la route et le gestionnaire de requêtes ensemble. Maintenant, nous démarrons l'application et visitons http://localhost:8888/start dans le navigateur. Le journal suivant peut montrer que le système appelle le bon gestionnaire de requêtes :

Copier. Code Le code est le suivant :

Le serveur a démarré.
Demande de /start reçue.
Sur le point d'acheminer une demande de /start
Le gestionnaire de requête 'start' a été appelé.

Et ouvrez http://localhost:8888/ dans le navigateur, vous pouvez voir que cette requête est également traitée par le gestionnaire de requête de démarrage :
Copier le code Le code est le suivant :

Demande de / reçue.
Sur le point d'acheminer une demande de /
Le gestionnaire de requêtes 'start' a été appelé.

Laissez le gestionnaire de requêtes répondre

Très bien. Mais maintenant, ce serait bien si le gestionnaire de requêtes pouvait renvoyer des informations significatives au navigateur au lieu de simplement « Hello World ».

Ce qu'il faut retenir ici, c'est que les informations "Hello World" obtenues et affichées après que le navigateur a fait une requête proviennent toujours de la fonction onRequest de notre fichier server.js.

En fait, « traiter une requête » signifie simplement « répondre à une requête ». Par conséquent, nous devons permettre au gestionnaire de requêtes de « parler » au navigateur comme la fonction onRequest.

Mauvaise mise en œuvre

Pour les développeurs comme nous ayant une formation technique PHP ou Ruby, la méthode d'implémentation la plus simple n'est en fait pas très fiable : elle semble efficace, mais en fait ce n'est peut-être pas le cas.

Ce que j'entends ici par « implémentation simple » est de permettre au gestionnaire de requêtes de renvoyer directement (return()) les informations qu'il souhaite afficher à l'utilisateur via la fonction onRequest.

Mettez-le d'abord en œuvre comme ceci, puis voyons pourquoi ce n'est pas une bonne façon de le mettre en œuvre.

Commençons par demander au gestionnaire de requêtes de renvoyer les informations qui doivent être affichées dans le navigateur. Nous devons modifier requestHandler.js sous la forme suivante :

Copier le code Le code est le suivant :

function start() {
console.log("Le gestionnaire de requête 'start' a été appelé.");
return "Bonjour Start";
}

function upload() {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
return "Bonjour Upload";
}

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


D'accord. De même, le routage des requêtes doit renvoyer au serveur les informations qui lui ont été renvoyées par le gestionnaire de requêtes. Par conséquent, nous devons modifier router.js sous la forme suivante :
Copiez le code Le code est le suivant :

function route(handle, pathname) {
console.log("À propos d'acheminer une requête pour "pathname);
if (typeof handle[pathname] === 'function') {
return handle [pathname]();
} else {
console.log("Aucun gestionnaire de requêtes trouvé pour "pathname);
return "404 Not found";
}
>

exports.route = route;


Comme indiqué dans le code ci-dessus, lorsque la requête ne peut pas être acheminée, nous renvoyons également certaines informations d'erreur associées.

Enfin, nous devons refactoriser notre server.js pour qu'il réponde au navigateur avec le contenu renvoyé par le gestionnaire de requêtes via la route de requête, comme ceci :

Copier le code Le code est le suivant :

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

function start(route, handle) {
function onRequest(request, réponse) {
var pathname = url.parse(request.url).pathname;
console.log("Demande de " chemin " reçu. ");

réponse.writeHead(200, {"Content-Type": "text/plain"});
var content = route(handle, pathname)
réponse.write(content);
réponse .end();
}

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;


Si nous exécutons l'application refactorisée, tout fonctionnera correctement : demandez http://localhost:8888/start, et le navigateur affichera "Hello Start", en demandant http ://localhost:8888/upload affichera "Hello Upload", tandis que la demande http://localhost:8888/foo affichera "404 Not found".

D'accord, alors quel est le problème ? Pour faire simple : lorsqu'un gestionnaire de requêtes devra effectuer des opérations non bloquantes à l'avenir, notre application se « bloquera ».

Vous ne comprenez pas ? Ce n’est pas grave, expliquons-le en détail ci-dessous.

Bloquant et non bloquant

Comme mentionné précédemment, des problèmes surviennent lors de l'inclusion d'opérations non bloquantes dans les gestionnaires de requêtes. Cependant, avant d'en parler, examinons d'abord ce que sont les opérations de blocage.

Je ne veux pas expliquer la signification spécifique de « bloquant » et « non-bloquant », regardons simplement ce qui se passe lorsque des opérations de blocage sont ajoutées au gestionnaire de requêtes.

Ici, modifions le gestionnaire de requête de démarrage. Nous le laissons attendre 10 secondes avant de renvoyer « Hello Start ». Puisqu'il n'existe pas d'opération comme sleep() en JavaScript, nous ne pouvons utiliser qu'un peu de Hack pour simuler l'implémentation.

Modifions requestHandlers.js sous la forme suivante :

Copiez le code Le code est le suivant :

function start() {
console.log("Le gestionnaire de requête 'start' a été appelé.");

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

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

function upload() {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
return "Bonjour Upload";
}

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


Dans le code ci-dessus, lorsque la fonction start() est appelée, Node.js attendra 10 secondes avant de renvoyer "Hello Start". Lorsque upload() est appelé, il reviendra immédiatement comme avant.

(Bien sûr, il ne s'agit que d'une simulation de sommeil pendant 10 secondes. Dans les scénarios réels, il existe de nombreuses opérations de blocage de ce type, comme certaines opérations de calcul à long terme.)

Jetons un coup d’œil aux changements que nos changements ont apportés.

Comme d'habitude, nous devons d'abord redémarrer le serveur. Afin de voir l'effet, nous devons effectuer quelques opérations relativement compliquées (suivez-moi) : Tout d'abord, ouvrez deux fenêtres ou onglets de navigateur. Saisissez http://localhost:8888/start dans la barre d'adresse de la première fenêtre du navigateur, mais ne l'ouvrez pas encore !

Entrez http://localhost:8888/upload dans la barre d'adresse de la deuxième fenêtre du navigateur Encore une fois, ne l'ouvrez pas encore !

Ensuite, procédez comme suit : appuyez sur Entrée dans la première fenêtre ("/start"), puis passez rapidement à la deuxième fenêtre ("/upload") et appuyez sur Entrée.

Remarquez ce qui s'est passé : l'URL /start a mis 10 secondes à se charger, ce à quoi nous nous attendions. Cependant, l'URL /upload prenait en réalité 10 secondes, et elle n'avait pas d'opération similaire à sleep() dans le gestionnaire de requêtes correspondant !

Pourquoi ça ? La raison est que start() contient des opérations de blocage. Pour le dire au sens figuré, "cela bloque tout autre travail de traitement".

C'est évidemment un problème, car Node s'est toujours annoncé ainsi : "Dans Node, à l'exception du code, tout est exécuté en parallèle."

Ce que cette phrase signifie, c'est que Node.js peut toujours traiter des tâches en parallèle sans ajouter de threads supplémentaires - Node.js est monothread. Il implémente des opérations parallèles via une boucle d'événements, et nous devrions en tirer pleinement parti - éviter autant que possible les opérations bloquantes et utiliser à la place des opérations non bloquantes.

Cependant, pour utiliser des opérations non bloquantes, nous devons utiliser des rappels en passant la fonction en paramètre à d'autres fonctions qui prennent du temps à traiter (par exemple, dormir pendant 10 secondes, ou interroger la base de données, ou effectuer une grande nombre de calculs).

Pour Node.js, c'est géré comme ceci : "Hé, probablementExpensiveFunction() (Note du traducteur : cela fait référence aux fonctions qui prennent du temps à traiter), vous continuez à vous occuper de vos affaires, je (fil de discussion Node. js) Je ne vous attendrai pas pour le moment. Je continuerai à traiter le code derrière vous. Veuillez fournir une fonction de rappel () une fois le traitement terminé. "

.

(Si vous souhaitez en savoir plus sur les sondages d'événements, vous pouvez lire l'article de blog de Mixu - Comprendre les sondages d'événements node.js.)

Ensuite, nous présenterons une mauvaise façon d'utiliser les opérations non bloquantes.

Comme la dernière fois, nous avons modifié notre application pour exposer le problème.

Cette fois, nous utilisons toujours le gestionnaire de requête de démarrage pour "opérer". Modifiez-le sous la forme suivante :

Copiez le code Le code est le suivant :

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

function start() {
console.log("Le gestionnaire de requête 'start' a été appelé.");
var content = "empty";

exec("ls -lah", function (erreur, stdout, stderr) {
content = stdout;
});

renvoyer le contenu ;
>

function upload() {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
return "Bonjour Upload";
}

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


Dans le code ci-dessus, nous avons introduit un nouveau module Node.js, child_process. La raison pour laquelle il est utilisé est d’implémenter une opération simple et pratique non bloquante : exec().

Que fait exec() ? Il exécute une commande shell à partir de Node.js. Dans l'exemple ci-dessus, nous l'utilisons pour obtenir tous les fichiers du répertoire actuel ("ls -lah"), puis nous envoyons les informations sur le fichier au navigateur lorsque /startURL est demandé.

Le code ci-dessus est très intuitif : créez une nouvelle variable content (la valeur initiale est "vide"), exécutez la commande "ls -lah", attribuez le résultat au contenu, et enfin renvoyez le contenu.

Comme d'habitude, nous démarrons le serveur et accédons à "http://localhost:8888/start".

Une belle page web sera chargée avec le contenu "vide". Que se passe-t-il?

À l'heure actuelle, vous avez peut-être deviné que exec() joue un rôle magique dans le non-blocage. C'est en fait une bonne chose, grâce à cela, nous pouvons effectuer des opérations shell très chronophages sans forcer notre application à s'arrêter et à attendre l'opération.

(Si vous voulez le prouver, vous pouvez remplacer "ls -lah" par une opération plus longue telle que "find /" pour obtenir l'effet).

Cependant, à en juger par les résultats affichés par le navigateur, nous ne sommes pas satisfaits de notre fonctionnement non bloquant, n'est-ce pas ?

D'accord, résolvons ensuite ce problème. Pendant que nous y sommes, voyons pourquoi l'approche actuelle ne fonctionne pas.

Le problème est que pour effectuer un travail non bloquant, exec() utilise une fonction de rappel.

Dans notre cas, la fonction de rappel est la fonction anonyme passée en deuxième paramètre à exec() :

Copier le code Le code est le suivant :

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

Maintenant, arrivons à la racine du problème : we Le code est exécuté de manière synchrone, ce qui signifie qu'après avoir appelé exec(), Node.js exécutera immédiatement le contenu de retour ; à ce moment, le contenu est toujours « vide » car la fonction de rappel passée à exec() n'a pas encore été exécutée ; . To - parce que le fonctionnement de exec() est asynchrone.

Le fonctionnement de "ls -lah" ici est en fait très rapide (sauf s'il y a des millions de fichiers dans le répertoire courant). C'est pourquoi la fonction de rappel sera exécutée rapidement - mais elle reste de toute façon asynchrone.

Pour rendre l'effet plus évident, imaginons une commande plus longue : "find /", qui prend environ 1 minute à s'exécuter sur ma machine. Cependant, bien que dans le gestionnaire de requêtes, je mette "ls - lah". est remplacé par "find /", lors de l'ouverture de l'URL /start, vous pouvez toujours obtenir la réponse HTTP immédiatement - évidemment, lorsque exec() est exécuté en arrière-plan, Node.js lui-même continuera à exécuter le code suivant. Et nous supposons ici que la fonction de rappel passée à exec() ne sera appelée qu'après l'exécution de la commande "find /".

Alors, comment pouvons-nous afficher la liste des fichiers dans le répertoire courant à l'utilisateur ?

D'accord, maintenant que nous comprenons cette mauvaise implémentation, voyons comment faire en sorte que le gestionnaire de requêtes réponde correctement aux requêtes du navigateur.

Réponse aux requêtes avec opérations non bloquantes

Je viens de mentionner l’expression – « la bonne voie ». En fait, la « bonne voie » n’est généralement pas simple.

Cependant, il existe une telle solution d'implémentation utilisant Node.js : le passage de fonctions. Voyons comment implémenter cela en détail.

Jusqu'à présent, notre application peut déjà transmettre le contenu renvoyé par le gestionnaire de requêtes (le gestionnaire de requêtes finira par afficher le contenu à l'utilisateur) est transmis au serveur HTTP.

Nous adoptons désormais la nouvelle méthode d'implémentation suivante : au lieu de transmettre le contenu au serveur, nous utilisons cette fois la méthode de "passer" le serveur au contenu. D'un point de vue pratique, l'objet de réponse (obtenu à partir de la fonction de rappel du serveur onRequest()) est transmis au gestionnaire de requêtes via le routage des requêtes. Le gestionnaire peut ensuite répondre à la demande en utilisant des fonctions sur cet objet.

C’est le principe, implémentons cette solution étape par étape.

Commencez avec server.js :

Copiez le code Le code est le suivant :

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

function start(route, handle) {
function onRequest(request, réponse) {
var pathname = url.parse(request.url).pathname;
console.log("Demande de " chemin " reçu. ");

route(handle, chemin, réponse);
}

http.createServer(onRequest).listen(8888);
console.log("Le serveur a démarré.");
}

exports.start = start;


Par rapport à la méthode précédente d'obtention de la valeur de retour de la fonction route(), cette fois nous passons l'objet de réponse comme troisième paramètre à la fonction route(), et nous allons supprimer tous les appels de fonction liés à la réponse dans le gestionnaire onRequest(), car nous voulons que cette partie du travail soit effectuée par la fonction route().

Jetons un œil à notre router.js :

Copiez le code Le code est le suivant :

function route(handle, pathname, réponse) {
console.log("À propos d'acheminer une demande pour "pathname);
if (typeof handle[pathname] === 'function') {
handle[pathname](response);
} else {
console.log("Aucun gestionnaire de requêtes trouvé pour "pathname);
réponse.writeHead(404, {"Content-Type" : "text /plain"});
response.write("404 Not found");
response.end();
}
}

exports.route = route;


Même modèle : au lieu d'obtenir la valeur de retour du gestionnaire de requêtes auparavant, cette fois, l'objet de réponse est transmis directement.

S'il n'y a pas de gestionnaire de requêtes correspondant, nous renverrons directement une erreur "404".

Enfin, nous modifions requestHandler.js sous la forme suivante :

Copiez le code Le code est le suivant :

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

function start(response) {
console.log("Le gestionnaire de requête 'start' a été appelé.");

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

function upload(response) {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
réponse.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Bonjour Upload");
response.end();
}

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


Notre fonction de gestionnaire doit recevoir le paramètre de réponse afin de répondre directement à la demande.

Le gestionnaire de démarrage effectue l'opération de réponse à la demande dans la fonction de rappel anonyme de exec(), tandis que le gestionnaire de téléchargement répond toujours simplement "Hello World", mais cette fois il utilise l'objet de réponse.

Maintenant, nous redémarrons l'application (node ​​​​index.js) et tout fonctionnera bien.

Si vous souhaitez prouver que les opérations fastidieuses dans le gestionnaire /start ne bloqueront pas la réponse immédiate à la requête /upload, vous pouvez modifier requestHandlers.js sous la forme suivante :

Copier le code Le code est le suivant :

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

function start(response) {
console.log("Le gestionnaire de requête 'start' a été appelé.");

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

function upload(response) {
console.log("Le gestionnaire de requête 'upload' a été appelé.");
réponse.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Bonjour Upload");
response.end();
}

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


De cette façon, lors de la demande de http://localhost:8888/start, le chargement prendra 10 secondes, et lors de la demande de http://localhost:8888/upload , il répondra immédiatement , même si la réponse /start est toujours en cours de traitement à ce moment-là.

Scénarios plus utiles

Jusqu'à présent, nous avons bien fait, cependant, notre application n'a aucune utilité pratique.

Le serveur, le routage des requêtes et le gestionnaire de requêtes sont terminés. Ajoutons une interaction au site Web selon le cas d'utilisation précédent : l'utilisateur sélectionne un fichier, télécharge le fichier, puis voit le fichier téléchargé dans le navigateur. Pour faire simple, nous supposons que l'utilisateur téléchargera uniquement une image et que notre application affichera cette image dans le navigateur.

D'accord, implémentons-le étape par étape Puisque nous avons déjà introduit de nombreux principes JavaScript et du contenu technique auparavant, accélérons un peu cette fois.

Pour implémenter cette fonction, il y a deux étapes suivantes : Tout d'abord, voyons comment gérer les requêtes POST (téléchargements hors fichiers). Après cela, nous utilisons un module externe de Node.js pour les téléchargements de fichiers. Il y a deux raisons à cette mise en œuvre.

Premièrement, bien que la gestion des requêtes POST de base dans Node.js soit relativement simple, vous pouvez encore apprendre beaucoup de choses au cours du processus.
Deuxièmement, utiliser Node.js pour gérer les téléchargements de fichiers (requêtes POST en plusieurs parties) est relativement complexe et sort du cadre de ce livre. Cependant, la façon d'utiliser des modules externes entre dans le cadre de ce livre.

Traitement des requêtes POST

Prenons un exemple simple : nous affichons une zone de texte pour que l'utilisateur puisse saisir du contenu, puis le soumettons au serveur via une requête POST. Enfin, le serveur reçoit la demande et affiche le contenu saisi au navigateur via le gestionnaire.

Le gestionnaire de requêtes

/start est utilisé pour générer un formulaire avec une zone de texte, nous modifions donc requestHandlers.js sous la forme suivante :

function start(response) {
console.log("Le gestionnaire de requête 'start' a été appelé.");

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

🎜> ''
' '
'
'
'
'';

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

response.write(body);
response.end();
}

function upload(response) {

console.log("Le gestionnaire de requête 'upload' a été appelé.");
réponse.writeHead(200, {"Content-Type": "text/plain"} );
response.write("Bonjour Upload");
response.end();
}

exports.start = start;

exports.upload = upload;
D'accord, maintenant notre application est très complète et peut même gagner des Webby Awards, haha. (Note du traducteur : les Webby Awards sont un prix parrainé par l'Académie internationale des arts et des sciences numériques pour sélectionner les meilleurs sites Web au monde. Veuillez consulter la description détaillée pour plus de détails) Vous pouvez le voir en visitant http://localhost:8888 /start dans votre navigateur C'est un formulaire simple, pensez à redémarrer le serveur !

Vous pourriez dire : Cette façon de placer des éléments visuels directement dans le gestionnaire de requêtes est trop moche. C'est vrai, mais je ne souhaite pas introduire de modèles tels que MVC dans ce livre, car cela n'est pas pertinent pour votre compréhension des environnements JavaScript ou Node.js.

Dans l'espace restant, nous aborderons un problème plus intéressant : lorsque l'utilisateur soumet le formulaire, le gestionnaire de requêtes /upload est déclenché pour gérer la requête POST.

Maintenant que nous sommes des experts parmi les novices, il est naturel de penser à utiliser des rappels asynchrones pour traiter les données des requêtes POST de manière non bloquante.

Le non bloquant est utilisé ici
Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Recommandations populaires
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal