node.js 程序_如何不使用外部程序包创建Node.js Web应用程序

 2023-09-06 阅读 23 评论 0

摘要:node.js 程序by Abhinav Pandey 通过Abhinav Pandey 如何不使用外部程序包创建Node.js Web应用程序 (How to create a Node.js web app using no external packages) 没有框架,没有NPM,没有Package.json,没有多余的装饰 (No frameworks, no NPM, no Pack

node.js 程序

by Abhinav Pandey

通过Abhinav Pandey

如何不使用外部程序包创建Node.js Web应用程序 (How to create a Node.js web app using no external packages)

没有框架,没有NPM,没有Package.json,没有多余的装饰 (No frameworks, no NPM, no Package.json, no frills)

In this post, we will dive deep inside Node.js fundamentals by creating a Node.js web app without any external packages. We will cover core concepts like streams, events, exceptions, HTTP etc.

在本文中,我们将通过创建一个没有任何外部包的Node.js Web应用程序来深入探究Node.js的基础知识 。 我们将涵盖流,事件,异常,HTTP等核心概念。

Currently, whenever we say we are going to implement a service in Node.js, most of the time we are going to use Express or other 3rd party libraries to implement our functionality. And I am not going to say there is any harm in doing that. These libraries provide necessary abstraction over redundant concepts which make us efficient.

当前,每当我们说要在Node.js中实现服务时,大多数时候我们将使用Express或其他第三方库来实现我们的功能。 而且我不会说这样做有任何危害。 这些库提供了使我们高效的冗余概念的必要抽象。

But with greater abstraction, the low-level logic of your program is hidden from you. As a result, we’re not able to develop a clear picture of how our business logic interacts with Node.js.

但是有了更大的抽象,您的程序的底层逻辑就被隐藏了。 结果,我们无法清楚地了解我们的业务逻辑如何与Node.js交互。

But as Ryan Dahl, the creator of Node.js said:

但是正如Node.js的创建者Ryan Dahl所说:

You can never understand everything. But, you should push yourself to understand the system.
您永远无法理解一切。 但是,您应该推动自己去理解系统。

We will push ourselves to form this clear picture in entirety.

我们将全力以赴形成这张清晰的图画。

So, let’s build a raw HTTP Node.js application with no Framework, no NPM, and no Package.json.

因此,让我们构建一个没有框架,没有NPM,也没有Package.json的原始HTTP Node.js应用程序。

We will build an app that will:

我们将构建一个应用程序,它将:

  1. Import necessary modules

    导入必要的模块

  2. Create a Server instance

    创建一个服务器实例

  3. Attach listeners to the request event of the server object

    将侦听器附加到服务器对象的request事件

  4. Parse request body and headers

    解析请求正文标头

  5. Sending Response to the client.

    客户端发送响应

  6. Handle error events at request and response streams.

    处理请求和响应流中的错误事件

But, here is the catch ;)

但是,这是要抓住的地方;)

We will do all of it from scratch with just

我们将仅从头开始做所有这一切

  1. a terminal, and

    一个终端,以及

  2. an editor.

    编辑。

Yes!! We will use nobody else’s framework, nobody else’s libraries just raw JavaScript and core Node.js runtime.

是!! 我们将使用其他人的框架其他人的库仅使用原始JavaScript和核心Node.js运行时

Let’s Begin!

让我们开始!

Before creating an HTTP server, let’s clear up the necessary concept of an HTTP module in Node.js.

在创建HTTP服务器之前,让我们在Node.js中清除HTTP模块的必要概念。

What is HTTP?

什么是HTTP?

http in Node.js is an inbuilt module that allows client-server communication via HTTP protocol. This module provides an interface to create either an HTTP client or HTTP server that can communicate with other HTTP servers or clients.

Node.js中的http是一个内置模块,允许通过HTTP协议进行客户端-服务器通信。 该模块提供了一个接口,以创建可以与其他HTTP服务器或客户端进行通信的HTTP客户端或HTTP服务器。

And to make this communication space efficient, an http module provides streaming of data using stream interface. And since stream passes data in chunks, that means Node.js never buffers the entire request or response at once in the memory. We will come to streams soon.

而为了让这个通信节省空间,一个http模块提供的 数据使用流接口。 而且由于流将数据分块传递,这意味着Node.js永远不会在内存中一次缓冲整个请求或响应。 我们将很快来到

So for our app, we will use this http interface to create an HTTP server that will listen to a particular port and give data back to the user.

因此,对于我们的应用程序,我们将使用此http接口创建一个HTTP服务器,该服务器将侦听特定端口并将数据返回给用户。

导入HTTP模块 (Importing the HTTP module)

To use either the http server or client you must require http module.

要使用http服务器或客户端,您必须使用http模块。

var http = require(“http”);

Now let us see how the above line actually works:

现在让我们看看上面的行实际上是如何工作的:

For loading an instance of a particular module in our runtime, Node.js provides us a require variable which is globally accessible. We use that globally defined require variable and tell Node to load the http module (by passing 'http' as the only param to the require function call).

为了在运行时中加载特定模块的实例,Node.js向我们提供了一个require变量,该变量可以全局访问。 我们使用全局定义的require变量,并告诉Node加载http模块(通过将'http'作为对require函数调用的唯一参数)。

There is a list of other globally available Node.js objects that you can check out in node REPL( by pressing <tab> twice).

您可以在节点REPL(通过按两次<tab>两次)中检出其他全局可用的Node.js对象的列表。

But the 2 most important for our use are:

但是,对于我们而言,最重要的两个是:

  1. The require module

    需求模块

  2. The module (in-depth explanation in the next article)

    模块 (下一篇文章中的深入说明)

(We do not need to require(‘require’) or require (‘module’) as they are global ).

(我们不需要require('require')require ('module')因为它们是global)。

How does require work?

require如何工作?

At runtime when Node.js invokes a require call (require(‘./path/to/fileName’), it searches for a file with a name the same as what’s provided in the only parameter to the require function call.

在运行时,当Node.js调用require调用(require('./ path / to / fileName')时,它将搜索名称与require函数调用唯一参数中提供的名称相同的文件。

And once the file name matches, Node.js checks for 3 types of extensions:

一旦文件名匹配,Node.js就会检查3种扩展名:

  1. .js — Node.js looks for “fileName.js” at the specified path to load as js script.

    .js — Node.js在指定路径下查找“ fileName.js”,以作为js脚本加载。

  2. .json — If Node.js finds “filename.json” file at the specified path it loads a file with the name corresponding to the value of the ‘main’ key in the JSON file.

    .json —如果Node.js在指定路径下找到“ filename.json”文件,它将加载一个名称与JSON文件中“ main”键值相对应的文件。

  3. .node — Node.js loads binary addons with name fileName.node at the specified path.

    .node — Node.js在指定路径加载名称为fileName.node的二进制插件。

创建一个服务器实例 (Create a Server instance)

Now that we have included the http module, we need to create an HTTP web server object. This can be done by using the createServer method on the http module.

现在我们已经包含了http模块,我们需要创建一个HTTP Web服务器对象。 这可以通过使用http模块上的createServer方法来完成。

To createServer method, we pass a callback function which is called every time a request is received on the server.

对于createServer方法,我们传递一个回调函数,该函数每次在服务器上收到请求时都会调用。

This createServer method returns a server object that we store in the variable app. This server object is an event emitter.

createServer方法返回一个服务器对象,该对象存储在变量app 。 该服务器对象是事件发射器。

Okay wait, what is an event emitter?

好吧,什么是event emitter

Let’s look a bit into the named event and emitter objects.

让我们看一下命名eventemitter对象。

Much of the Node.js core APIs are built around an event-driven architecture. Certain kinds of objects (called “emitters”) can cause some Function (“listeners”) to be called by emitting any “named” events.

许多Node.js核心API都是基于事件驱动的体系结构构建的。 某些类型的对象(称为“发射器”)可以通过发出任何“命名”事件来导致某些功能(“侦听器”)被调用。

Let us see an example to get the hang of it.

让我们看一个例子来说明问题。

Output : Called namedEvent in myEventObject’s attached listner

输出: Called namedEvent in myEventObject's attached listner

Explanation

说明

In the above example, we saw the namedEvent has a listener (a function) attached to it. By attached, we mean the listener is called after it hears the named event. So the listener prints the output on the console screen when the emitters object emits namedEvent.

在上面的示例中,我们看到namedEvent了一个侦听器(一个函数)。 所谓附加,是指侦听器在听到命名事件之后被调用。 因此,当发射器对象发出namedEvent时,侦听器在控制台屏幕上打印输出。

Apart from attaching the listeners, the eventEmitter object provides many other properties and functions such as

除了附加侦听器外, eventEmitter对象还提供许多其他属性和功能,例如

  • you can get the count of the total number of listeners attached to a named event, or

    您可以获得连接到命名事件的侦听器总数的计数,或者
  • you can also remove a Listener attached to the events.

    您还可以删除附加到事件的侦听器。

You can refer to the Node.js official docs for more detailed information about events in Node.js.

您可以参考Node.js官方文档,以获取有关Node.js中事件的更多详细信息。

Moving back to our example…

回到我们的例子……

Our web server object is also like all other emitter objects implementing event emitter interfaces. It also emits different kinds of named events.

我们的Web服务器对象也与实现事件发射器接口的所有其他发射器对象一样。 它还发出不同种类的命名事件。

Some of them are the following:

其中一些如下:

  1. connect— raised for all the ‘connect’ request by the HTTP client.

    connect —由HTTP客户端针对所有“连接”请求引发。

  2. connection — Emitted when a new TCP stream is established. Provide access to the socket established.

    connection -建立新的TCP流时发出。 提供对建立的套接字的访问。

  3. request — Emitted for Each request from the client (We would listen here).

    request —针对客户端的每个请求发出的请求(我们将在此处侦听)。

  4. upgrade — emitted each time a client requests an upgrade of the protocol (can be HTTP version).

    upgrade -每次客户端请求协议upgrade发出的消息(可以是HTTP版本)。

You can get the complete list of events emitted by our web server from the official Node.js docs.

您可以从官方的Node.js文档中获取我们的Web服务器发出的事件的完整列表。

监听请求事件 (Listening to the request event)

Now since our server needs to listen to the incoming request, we will listen to the request event of our HTTP server.

现在,由于我们的服务器需要侦听传入的请求,因此我们将侦听HTTP服务器的request事件。

Code Sample:

代码样例:

In the 3rd line, a listener function is attached to listen to all the request events on our server object.

在第三行中,附加了一个侦听器功能,以侦听服务器对象上的所有request事件。

The request event provides the listener function with 2 parameters which are:

request事件为侦听器功能提供2个参数,它们是:

  1. request — an instance of http.incomingMessage object and

    request — http.incomingMessage对象的实例,以及

  2. response — an instance of http.ServerResponse object.

    response — http.ServerResponse对象的实例。

These request and response objects have properties and methods that they inherit from the http.incomingMessage and http.ServerResponse classes, respectively.

这些requestresponse对象具有分别从http.incomingMessagehttp.ServerResponse类继承的属性和方法。

解析请求正文标头 (Parse request body and headers)

Now that we have access to request and response object…

现在我们可以访问requestresponse对象了……

The first few things that you might want to know about incoming requests are the URL, method, and Headers. Node.js makes it very easy by attaching them as properties to the request object (passed as the first parameter for the listener of request event).

您可能想了解传入请求的前几件事是URL,方法和Headers 。 通过将它们作为属性附加request对象(作为request事件的侦听器的第一个参数传递),Node.js使其变得非常容易。

You can de-structure the request object to get them out like this:

您可以对请求对象进行解构以将其释放,如下所示:

const {headers, url, method } = request;

const {headers, url, method } = request;

headers passed in request are present as an independent object inside the request object (secret : they are all in lower-case).

headers在请求中传递的存在形式与内一个独立的对象request对象(秘密:它们都在小写)。

After looking at the http method, in case of a PUT or POST request, we are interested in looking at the data sent in the request body.

查看http方法后,如果有PUT或POST请求,我们有兴趣查看请求正文中发送的data

But to take the data out of the request body we need to know a few key points about the request object.

但是要将数据带出请求主体,我们需要了解有关请求对象的一些关键点。

Request Object — a readable stream

请求对象-可读流

The request object that’s passed into the handler also implements the readable stream interface. This means that our request object is a stream that can be listened to or piped elsewhere to grab the data that is flowing into it. We will also grab the data right out of the request stream by listening to the stream’s data and end events.

传递到处理程序中的request对象还实现了可读流接口。 这意味着我们的request对象是一个流,可以在其他地方监听或通过管道传输以获取流入其中的数据。 我们还将通过侦听request流的dataend事件,从request流中直接获取数据。

Different kinds of data may be passed to our server, but to keep it simple we will be passing only the string in the body.

可能会将不同类型的数据传递到我们的服务器,但是为了简单起见,我们将仅传递正文中的字符串。

To use that data we need to parse it, so we will be using the data and end event of the readable stream which is implemented by our request object as mentioned earlier.

要使用该数据,我们需要对其进行解析 ,因此我们将使用由我们的request对象实现的可读流的dataend事件,如前所述。

On each data event, the readable stream passes data as a buffer chunk. We will be appending all the chunks in an empty array. And at the end event we will concat and stringify the array to get the cumulative body.

在每个data事件中, 可读流将数据作为缓冲区块传递。 我们将所有块附加到一个空数组中。 并在end时,我们将Concat的和字符串化阵列以获得累积体。

So here is the code up until now:

所以这是到目前为止的代码:

将响应发送给客户端。 (Sending the Response to the client.)

After collecting data from the HTTP request we need to give an appropriate response to the client. But since the request object implements a readable stream only, we need a writable stream where we can write out our response.

从HTTP请求中收集数据后,我们需要对客户端进行适当的响应。 但是由于request对象仅实现可读流,因此我们需要可写流,在其中可以写出响应。

Response Object — a writable stream

响应对象-可写流

For doing so, Node.js provide us with a 2nd parameter that is the response object to the request event listener.

为此,Node.js为我们提供了第二个参数,它是request事件侦听器的response对象。

By using the response object, we can set HTTP status code, set headers, and write content in the write stream of the response object.

通过使用response对象,我们可以设置HTTP状态代码,设置标头并在响应对象的写入流中写入内容。

Although if you do not set the response code explicitly, then Node.js itself sets it to 200. But as complexity increases you will want to set the desired statusCode of the HTTP response.

尽管如果您未明确设置响应代码,则Node.js本身会将其设置为200。但是随着复杂度的增加,您将需要设置HTTP响应的所需statusCode

Implicit headers setting

隐式标题设置

You can set, get and remove headers to the response using setHeader(name, value), getHeader(name), and removeHeader(name) API.

您可以使用setHeader(name, value)getHeader(name)removeHeader(name) API 设置,获取删除响应的标头。

Code Sample:

代码样例:

When using the above setHeader() method for setting headers, we are depending on Node.js to implicitly set the response headers before sending the response body.

当使用上述setHeader()方法设置标头时,我们依赖于Node.js在发送响应主体之前隐式设置响应标头。

To set headers and status code explicitly, we have a response.writeHead() method.

为了显式 设置标题和状态代码我们有一个response.writeHead()方法。

Code Sample:

代码样例:

While explicitly setting headers we should keep in mind that headers come before the body in the HTTP response. That is, we should prefer using the writeHead() method before writing anything to the response body.

在显式设置标头时,我们应记住, 标头位于HTTP响应的主体之前 。 也就是说,在将任何内容写入响应主体之前,我们应该首选使用writeHead()方法。

Now let us see how we can write data to a response.

现在让我们看看如何将数据写入响应。

Since the response object is a writable stream object, we just need to use write stream methods to write data chunks into the HTTP response object.

由于响应对象是可写的流对象,因此我们只需要使用写流方法将数据块写入HTTP响应对象。

Code Sample:

代码样例:

After writing to the response stream, we need to close the stream so that Node.js gets to know that it’s time to send the response back to the client.

写入响应流之后,我们需要关闭该流,以便Node.js知道是时候将该响应发送回客户端了。

.end() method allows us to close the HTTP connection that was set up at the time of the request hitting our server. The end() method also accepts a last string to be written before closing the connection.

.end()方法允许我们关闭在请求到达服务器时建立的HTTP 连接end()方法还接受在关闭连接之前要写入的最后一个字符串。

If we do not use the end method, Node.js will write data to the write stream and wait…

如果不使用end方法,Node.js会将数据写入写流并等待…

…until the default timeout in the server object expires. That is, for any request, Node.js only waits for a fixed time (which is specified in the server object) before closing the connection. And once the connection is closed (either manually using end() or the timeout expires), Node frees up all the allocated resources immediately.

…直到服务器对象中的默认超时 到期。 也就是说,对于任何请求, Node.js仅 在关闭连接之前 等待固定时间 (在服务器对象中指定)。 一旦关闭连接(手动使用end()或超时到期), Node就会立即释放所有分配的资源

You can set or change the timeout using server.setTimeout([msecs][, callback]).

您可以使用server.setTimeout([msecs][, callback])设置或更改超时。

To disable the timeout, you can set the timeout value to 0. But as the timeout is assigned at the time of forming a new connection, the timeout will only be updated for upcoming new connections.

要禁用超时,可以将超时值设置为0。但是由于在建立新连接时分配了超时 ,所以只会为即将到来的新连接更新超时

Now that we have written our response, our server should work fine.

现在我们已经写了响应,我们的服务器应该可以正常工作了。

但是,当我们的服务器遇到异常时会发生什么? (But, what will happen when our server encounters an exception?)

We need to hear the error events of request and response streams. An error event is raised every time an exception occurs. You can try to avoid it but they do come and we have to catch and handle them properly.

我们需要听到requestresponse流的error事件。 每次发生异常时都会引发一个error事件。 您可以尝试避免它,但是它们确实来了,我们必须抓住并正确处理它们。

But how?

但是如何?

We will handle them by attaching error handlers to the error events of request and response streams.

我们将通过将错误处理程序附加requestresponse流的error事件来处理它们。

Explanation

说明

Here we are catching all the error events of request and response streams and just logging them into the console. You can also use util instead of the console in the production environment (although in production it’s advised to inspect errors properly).

在这里,我们捕获了requestresponse流的所有error事件,并将它们记录到控制台中。 您还可以在生产环境中使用util而不是console (尽管建议在生产环境中正确检查错误)。

Now let us have a look at the code sample we have up til now.

现在,让我们看一看直到现在为止的代码示例。

Okay so our server is capable of the following things at this point:

好的,此时我们的服务器可以执行以下操作:

  1. Import necessary modules

    导入必要的模块

  2. create a Server instance

    创建一个服务器实例

  3. Attach listeners to the request event of server object

    将侦听器附加到服务器对象的request事件

  4. Parse request body and headers

    解析请求正文标头

  5. Write the response to response Stream

    将响应写入响应流

  6. Handle error events at request and response streams.

    处理请求和响应流中的错误事件

By now we have made our server object capable of taking head on to new connections but we have not told it where to listen for new connections. That is, this server object also needs to be bound to a particular port so that our server can have access to all the incoming requests at that port.

到现在为止,我们已经使服务器对象能够进行新连接,但是我们还没有告诉它在哪里监听新连接。 也就是说,该服务器对象还需要绑定到特定端口,以便我们的服务器可以访问该端口上的所有传入请求。

To do so we will use the .listen method of our HTTP server object, .listen(PORT , CB).

为此,我们将使用HTTP服务器对象.listen(PORT , CB)..listen方法.listen(PORT , CB).

@params PORT is the port number where we want our server to listen.

@params PORT是我们希望服务器监听的端口号。

@params Callback is called once the server starts listening.

服务器开始侦听后,将调用@params回调。

Code Sample:

代码样例:

By now our server is ready to receive requests.

至此,我们的服务器已准备就绪,可以接收请求。

Let us run our Node.js app:

让我们运行Node.js应用程序:

node app.js

And hit our server with the following curl on a terminal:

并在终端上通过以下卷曲命中我们的服务器:

curl -d “Hello World” -H “Content-Type: text” -X POST http://localhost:8008

WooHoo!! Congratulations, You have created a Node.js app without any external packages.

呜呜! 恭喜,您已经创建了一个没有任何外部程序包的Node.js应用程序。

It is wonderfully applaudable that you stayed this long.

你们住了这么长时间真是值得称赞。

If you are willing to learn more about the Node.js core like this, then let me know by bursting the claps counts to 50.

如果您愿意像这样学习有关Node.js核心的更多信息,请通过将鼓掌数增加到50来告诉我。

In the next articles, we will continue building over this basic app and add other critical features of routing, middleware, error handling etc. Get notified by following me here on Medium.

在接下来的文章中,我们将继续构建此基本应用程序,并添加路由,中间件,错误处理等其他关键功能。

I have tried to make this article as complete as possible. If you have any ideas that could make it better, please mention in your valuable comments.

我试图使本文尽可能完整。 如果您有任何可以改善的想法,请在有价值的评论中提及。

You can connect me via gmail or tweet me here.

您可以通过gmail与我联系,或者在这里向我发送推文。

Thank you so much for your love! Pardon my mistakes, you have been a wonderful audience.

非常感谢您的爱! 请原谅我的错误,您真是一个很棒的听众。

翻译自: https://www.freecodecamp.org/news/a-no-frills-guide-to-node-js-how-to-create-a-node-js-web-app-without-external-packages-a7b480b966d2/

node.js 程序

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/5/7475.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息