深入理解nodejs的HTTP处理流程
简介
我们已经知道如何使用nodejs搭建一个HTTP服务,今天我们会详细的介绍nodejs中的HTTP处理流程,从而对nodejs的HTTP进行深入的理解。
使用nodejs创建HTTP服务
使用nodejs创建HTTP服务很简单,nodejs提供了专门的HTTP模块,我们可以使用其中的createServer方法来轻松创建HTTP服务:
const http = require('http');
const server = http.createServer((request, response) => {
// magic happens here!
});
首先createServer方法传入的是一个callback函数,这个callback函数将会在每次服务端接收到客户端的请求时调用。所以这个callback函数,也叫做 request handler.
再看看createServer的返回值,createServer返回的是一个EventEmitter对象。
之前我们也介绍过了EventEmitter,它可以发送和接收事件,所以我们可以使用on来监听客户端的事件。
上面的代码相当于:
const server = http.createServer();
server.on('request', (request, response) => {
// the same kind of magic happens here!
});
当发送request事件的时候,就会触发后面的handler method,并传入request和response参数。我们可以在这个handler中编写业务逻辑。
当然,为了让http server正常运行,我们还需要加上listen方法,来绑定ip和端口,以最终启动服务。
const hostname = '127.0.0.1'
const port = 3000
server.listen(port, hostname, () => {
console.log(`please visit http://${hostname}:${port}/`)
})
解构request
上面的request参数实际上是一个http.IncomingMessage对象,我们看下这个对象的定义:
class IncomingMessage extends stream.Readable {
constructor(socket: Socket);
aborted: boolean;
httpVersion: string;
httpVersionMajor: number;
httpVersionMinor: number;
complete: boolean;
/**
* @deprecate Use `socket` instead.
*/
connection: Socket;
socket: Socket;
headers: IncomingHttpHeaders;
rawHeaders: string[];
trailers: NodeJS.Dict<string>;
rawTrailers: string[];
setTimeout(msecs: number, callback?: () => void): this;
/**
* Only valid for request obtained from http.Server.
*/
method?: string;
/**
* Only valid for request obtained from http.Server.
*/
url?: string;
/**
* Only valid for response obtained from http.ClientRequest.
*/
statusCode?: number;
/**
* Only valid for response obtained from http.ClientRequest.
*/
statusMessage?: string;
destroy(error?: Error): void;
}
通常我们需要用到request中的method,url和headers属性。
怎么从request中拿到这些属性呢?对的,我们可以使用ES6中解构赋值:
const { method, url } = request;
const { headers } = request;
const userAgent = headers['user-agent'];
其中request的headers是一个IncomingHttpHeaders,它继承自NodeJS.Dict。
处理Request Body
从源码可以看出request是一个Stream对象,对于stream对象来说,我们如果想要获取其请求body的话,就不像获取静态的method和url那么简单了。
我们通过监听Request的data和end事件来处理body。
let body = [];
request.on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// at this point, `body` has the entire request body stored in it as a string
});
因为每次data事件,接收到的chunk实际上是一个Buffer对象。我们将这些buffer对象保存起来,最后使用Buffer.concat来对其进行合并,最终得到最后的结果。
直接使用nodejs来处理body看起来有点复杂,幸运的是大部分的nodejs web框架,比如koa和express都简化了body的处理。
处理异常
异常处理是通过监听request的error事件来实现的。
如果你在程序中并没有捕获error的处理事件,那么error将会抛出并终止你的nodejs程序,所以我们一定要捕获这个error事件。
request.on('error', (err) => {
// This prints the error message and stack trace to `stderr`.
console.error(err.stack);
});
解构response
response是一个http.ServerResponse类:
class ServerResponse extends OutgoingMessage {
statusCode: number;
statusMessage: string;
constructor(req: IncomingMessage);
assignSocket(socket: Socket): void;
detachSocket(socket: Socket): void;
// https://github.com/nodejs/node/blob/master/test/parallel/test-http-write-callbacks.js#L53
// no args in writeContinue callback
writeContinue(callback?: () => void): void;
writeHead(statusCode: number, reasonPhrase?: string, headers?: OutgoingHttpHeaders): this;
writeHead(statusCode: number, headers?: OutgoingHttpHeaders): this;
writeProcessing(): void;
}
对于response来说,我们主要关注的是statusCode:
response.statusCode = 404;
Response Headers:
response提供了setHeader方法来设置相应的header值。
response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'bacon');
还有一个更加直接的同时写入head和status code:
response.writeHead(200, {
'Content-Type': 'application/json',
'X-Powered-By': 'bacon'
});
最后,我们需要写入response body,因为response是一个WritableStream,所以我们可以多次写入,最后以end方法结束:
response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
或者我们可以用一个end来替换:
response.end('<html><body><h1>Hello, World!</h1></body></html>');
综上,我们的代码是这样的:
const http = require('http');
http.createServer((request, response) => {
const { headers, method, url } = request;
let body = [];
request.on('error', (err) => {
console.error(err);
}).on('data', (chunk) => {
body.push(chunk);
}).on('end', () => {
body = Buffer.concat(body).toString();
// BEGINNING OF NEW STUFF
response.on('error', (err) => {
console.error(err);
});
response.statusCode = 200;
response.setHeader('Content-Type', 'application/json');
// Note: the 2 lines above could be replaced with this next one:
// response.writeHead(200, {'Content-Type': 'application/json'})
const responseBody = { headers, method, url, body };
response.write(JSON.stringify(responseBody));
response.end();
// Note: the 2 lines above could be replaced with this next one:
// response.end(JSON.stringify(responseBody))
// END OF NEW STUFF
});
}).listen(8080);
本文作者:flydean程序那些事
本文链接:http://www.flydean.com/nodejs-http-in-depth/
本文来源:flydean的博客
欢迎关注我的公众号:「程序那些事」最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
深入理解nodejs的HTTP处理流程的更多相关文章
- 从原理上理解NodeJS的适用场景
NodeJS是近年来比较火的服务端JS平台,这一方面得益于其在后端处理高并发的卓越性能,另一方面在nodeJS平台上的npm.grunt.express等强大的代码与项目管理应用崛起,几乎重新定义了前 ...
- 轻松理解Redux原理及工作流程
轻松理解Redux原理及工作流程 Redux由Dan Abramov在2015年创建的科技术语.是受2014年Facebook的Flux架构以及函数式编程语言Elm启发.很快,Redux因其简单易学体 ...
- [NodeJs系列][译]理解NodeJs中的Event Loop、Timers以及process.nextTick()
译者注: 为什么要翻译?其实在翻译这篇文章前,笔者有Google了一下中文翻译,看的不是很明白,所以才有自己翻译的打算,当然能力有限,文中或有错漏,欢迎指正. 文末会有几个小问题,大家不妨一起思考一下 ...
- 深入理解nodejs中的异步编程
目录 简介 同步异步和阻塞非阻塞 javascript中的回调 回调函数的错误处理 回调地狱 ES6中的Promise 什么是Promise Promise的特点 Promise的优点 Promise ...
- 【.NET Core项目实战-统一认证平台】第十二章 授权篇-深入理解JWT生成及验证流程
[.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章介绍了基于Ids4密码授权模式,从使用场景.原理分析.自定义帐户体系集成完整的介绍了密码授权模式的内容,并最后给出了三个思考问题,本 ...
- 理解nodejs的module模块儿
module 在 Node.js 模块系统中,每个文件都视为独立的模块,node在运行某个模块儿时会生成一个module对象 Module { id: '.', exports: 2, parent: ...
- 理解Nodejs的Event Loop
Node的“event loop”主要是用来处理高输出量的.这很神奇,这也是为什么node可以在单线程的情况下同时处理很多的后台操作.本文就会集中讲述event loop是怎么运行的,这样你可以可以使 ...
- Nodejs中使用异步流程控制Async
首先,我们都知道,Node基于事件驱动的异步I/O架构,所谓异步就是非阻塞,说白了就是一个事件执行了,我不必等待它执行完成后我才能执行下一个事件.所以在Node环境中的模块基本都是异步的,上一篇说到我 ...
- 77.深入理解nodejs中Express的中间件
转自:https://blog.csdn.net/huang100qi/article/details/80220012 Express是一个基于Node.js平台的web应用开发框架,在Node.j ...
随机推荐
- flume基本概念及相关参数详解
1.flume是分布式的日志收集系统,把手机来的数据传送到目的地去 2.flume传输的数据的基本单位是 event,如果是文本文件,通常是一行记录. event代表着一个数据流的最小完整 ...
- SpringCloud 源码系列(6)—— 声明式服务调用 Feign
SpringCloud 源码系列(1)-- 注册中心 Eureka(上) SpringCloud 源码系列(2)-- 注册中心 Eureka(中) SpringCloud 源码系列(3)-- 注册中心 ...
- Tensorflow--------tf.nn库
1.tf.nn :提供神经网络相关操作,包括卷积神经(conv),池化操作(pooling),归一化,loss,分类操作,embedding,RNN,Evaluation. 2.tf.layers:高 ...
- 壁球小游戏详解(附有源码.cpp)
1.在python中安装pygame 2.将下列源码复制过去,运行. #引用 import pygame, sys #初始化 pygame.init() size = width, height = ...
- 用python做youtube自动化下载器 代码
目录 项目地址 思路 流程 1. post i. 先把post中的headers格式化 ii.然后把参数也格式化 iii. 最后再执行requests库的post请求 iv. 封装成一个函数 2. 调 ...
- .NET Core引入日志(Log4Net篇)
Demo版本信息如下: VS:2019 框架:.Net Core 3.1 Log4Net:2.0.12 思维导图: [1]添加依赖项 通过nuget添加Log4Net [2]创建公共类 添加公共类Lo ...
- CSS不用背景图片实现优惠券样式反圆角,凹圆角,反向半圆角,并且背景渐变
日常开发过程中,特别是商城相关应用开发过程中,时常会遇到花里胡哨的设计图,比如优惠券样式,上图: 实现思路如下: 1.先写一个外容器,实现背景色渐变: Html: 1 <div clas ...
- FTP使用Socket SSL流程认证(一)
关于Ftp使用SSL流程认证 本文章使用的是C#,ftp服务器为FileZilla 注:如果不是使用的Socket可以使用FtpWebRequst类,说实话,该类比较简单,但现在说的是SOCKET,网 ...
- 转 2 jmeter常用功能介绍-测试计划、线程组
2 jmeter常用功能介绍-测试计划.线程组 1.测试计划测试用来描述一个性能测试,所有内容都是基于这个测试计划的. (1)User Defined Variables:设置用户全局变量.一般添 ...
- Vue基础之生命周期函数[残缺版]!
Vue基础之生命周期函数[残缺版]! 为什么说是残缺版呢?! 因为有一些周期函数我并没有学到!所以是残缺版! 01 beforeCreate //在实例初始化之后,数据观测 (data observe ...