Node入门教程(12)第十章:Node的HTTP模块
Ryan Dahl开发node的初衷就是:把Nginx
非阻塞IO功能和一个高度封装的WEB服务器结合在一起的东东。所以Node初衷就是为了高性能的Web服务器去的,所以:Node的HTTP模块也是核心的核心。
本文需要您了解的前置知识点:
- HTTP协议
- Web请求模型:请求→处理→响应
- Node的流、事件
http模块的客户端
要使用 HTTP 服务器与客户端,需要 require('http')
模块。http模块提供了两个函数http.request()
和http.get()
,帮助程序向服务器端发送请求。
我们可以通过http.request ()
方法创建一个发送请求的http.ClientRequest
类实例,请求创建后,并不会立即发送请求,我们还可以继续访问请求头:setHeader(name, value)
、getHeader(name)
和 removeHeader(name)
API 进行修改。实际的请求头会与第一个数据块一起发送或当调用 request.end()
时发送。
http.ClientRequest类
http.ClientRequest
类继承了EventEmitter
,它内部定义了以下事件。
事件 | 说明 |
---|---|
abort | 当请求已被客户端终止时触发。 该事件仅在首次调用 abort() 时触发。 |
connect |
每当服务器响应 CONNECT 请求时触发。 如果该事件未被监听,则接收到 CONNECT 方法的客户端会关闭连接。 |
continue |
当服务器发送了一个 100 Continue 的 HTTP 响应时触发, 通常是因为请求包含 Expect: 100-continue。 这是客户端将要发送请求主体的指令。 |
response |
当请求的响应被接收到时触发。 该事件只触发一次。如果没有添加 'response' 事件处理函数,则响应会被整个丢弃。 如果添加了 'response' 事件处理函数,则必须消耗完响应对象的数据, 可通过调用 response.read()、或添加一个 'data' 事件处理函数、 或调用 .resume() 方法。 数据被消耗完时会触发 'end' 事件。 在数据被读取完之前会消耗内存,可能会造成 'process out of memory' 错误。 |
socket | 当 socket 被分配到请求后触发。 |
timeout |
当底层 socket 超时的时候触发。 该方法只会通知空闲的 socket。请求必须手动停止。 |
upgrade |
每当服务器响应 upgrade 请求时触发。 如果该事件未被监听,则接收到 upgrade 请求头的客户端会关闭连接。 |
http.ClientRequest
类还提供了一些方法供我们进行请求和返回响应的处理。
方法 | 参数 | 说明 |
---|---|---|
request.end([data[, encoding]][, callback]) |
①data 发送的数据 ②encoding 编码 ③callback回调函数 |
结束发送请求。如果部分请求主体还未被发送,则会刷新它们到流中。如果请求是分块的,则会发送终止字符 '0\r\n\r\n'。如果指定了 data,则相当于调用 request.write(data, encoding) 之后再调用 request.end(callback)。如果指定了 callback,则当请求流结束时会被调用。 |
request.flushHeaders() |
无 | 刷新请求头。出于效率的考虑,Node.js 通常会缓存请求头直到 request.end() 被调用或第一块请求数据被写入。 然后 Node.js 会将请求头和数据打包成一个单一的 TCP 数据包。 |
request.getHeader(name) |
①name ②返回字符串 | 读出请求头,注意:参数name是大小写敏感的 |
request.removeHeader(name) |
name 字符串 | 移除一个已经在 headers 对象里面的 header。 |
request.setHeader(name, value) |
①name是header的key②value | 为 headers 对象设置一个单一的 header 值。如果该 header 已经存在了,则将会被替换。这里使用一个字符串数组来设置有相同名称的多个 headers。 |
request.setSocketKeepAlive([enable][, initialDelay]) |
①enable类型boolean②initialDelay | 一旦 socket 被分配给请求且已连接,socket.setKeepAlive() 会被调用。 |
request.setTimeout(timeout[, callback]) |
①timeout请求被认为是超时的毫秒数。②callback 可选的函数,当超时发生时被调用。 | 等同于绑定到 timeout 事件。一旦socket被分配给请求且已连接,socket.setTimeout() 会被调用。 |
request.write(chunk[, encoding][, callback]) |
①chunk发送的请求数据。②encoding:编码;③callback回调函数 | 发送请求主体的一个数据块。 通过多次调用该方法,一个请求主体可被发送到一个服务器,在这种情况下,当创建请求时,建议使用 ['Transfer-Encoding', 'chunked'] 请求头。 |
发送GET请求
// 引入http模块
const http = require('http');
// 创建一个请求
let request = http.request(
{
protocol: 'http:', // 请求的协议
host: 'aicoder.com', // 请求的host
port: 80, // 端口
method: 'GET', // GET请求
timeout: 2000, // 超时时间
path: '/' // 请求路径
},
res => { // 连接成功后,接收到后台服务器返回的响应,回调函数就会被调用一次。
// res => http.IncomingMessage : 是一个Readable Stream
res.on('data', data => {
console.log(data.toString('utf8')); // 打印返回的数据。
});
}
);
// 设置请求头部
request.setHeader('Cache-Control', 'max-age=0');
// 真正的发送请求
request.end();
发送get请求的另外一个办法
http模块还提供了http.get(options,callback)
,用来更简单的处理GET方式的请求,它是http.request()
的简化版本,唯一的区别在于http.get自动将请求方法设为GET请求,同时不需要手动调用req.end();
http.get('http://aicoder.com', res => {
res.on('data', data => {
console.log(data.toString('utf8'));
});
});
发送post请求
且看一个发送post请求的例子。
const http = require('http');
let request = http.request(
{
protocol: 'http:',
host: 'aicoder.com',
port: 80,
method: 'POST',
timeout: 2000,
path: '/'
},
res => {
res.on('data', data => {
console.log(data.toString('utf8'));
});
}
);
// 发送请求的数据。
request.write('id=3&name=aicoder');
request.end();
HTTP服务器端
http.Server
实现了简单的web服务器,并把请求和响应也做了封装。
http.server对象的事件
http.server
是一个基于事件的HTTP服务器,所有的请求都被封装到独立的事件当中,我们只需要对他的事件编写相应的行数就可以实现HTTP服务器的所有功能,它继承自EventEmitter,提供了以下的事件:
request
:当客户端请求到来的时候,该事件被触发,提供两个参数request和response,分别是http.ServerRequest和http.ServerResponse表示请求和响应的信息。connection
:当TCP建立连接的时候,该事件被触发,提供了一个参数socket,为net.socket的实例(底层协议对象)close
:当服务器关闭的时候会被触发- 除此之外还有
checkContinue
、upgrade
、clientError
等事件
我们最常用的还是request
事件,http也给这个事件提供了一个捷径:http.createServer([requestListener])
下面我们来简单的看一下两个案例:
第一个:使用request事件的:
const http = require('http');
let server = new http.Server();
server.on('request', (req, res) => {
console.log(req.url);
//设置应答头信息
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('hello we are family<br>');
res.end('server already end\n');
});
//显示了三次这也证明了TCP的三次握手
server.on('connection', () => {
console.log('握手');
});
server.on('close', () => {
console.log('server will close');
});
//关闭服务为了触发close事件
server.close();
server.listen(8080);
第二个:利用http.createServer
创建服务器实例代码:
const http = require('http');
http.createServer(function(req,res){
res.writeHead(200,{'Content-Type':'text/plain'})
res.write("hi, from aicoder.com");
res.end();
}).listen(3000);
http.ServerRequset请求信息
我们都知道HTTP请求分为两部分:请求头和请求体,如果请求的内容少的话就直接在请求头协议完成之后立即读取,请求体可能相对较长一点,需要一定的时间传输。因此提供了三个事件用于控制请求体传输.
1.data
:当请求体数据到来时,该事件被触发,该事件一共一个参数chunk,表示接受到的数据。
1.end
:当请求体数据传输完成时,该事件被触发,此后将不会再有数据到来。
1.close
:用户当前请求结束时,该事件被触发,不同于end,如果用户强制终止了传输,也会触发close
ServerRequest的属性
名称 | 含义 |
---|---|
ccomplete | 客户端请求是否已经发送完成 |
httpVersion | HTTP协议版本,通常是1.0或1.1 |
method | HTTP请求方法,如:GET,POST |
url | 原始的请求路径 |
headers | HTTP请求头 |
trailers | HTTP请求尾(不常见) |
connection | 当前HTTP连接套接字,为net.Socket的实例 |
socket | connection属性的别名 |
client | client属性的别名 |
http.createServer(function(req,res){
console.log(req.httpVersion);
//console.log(req.socket);
console.log(req.headers);
console.log(req.method);
res.writeHead(404,{'Content-Type':'text/plain'})
res.write("we are is content");
res.end();
}).listen(8080);
获取GET请求内容
由于GET请求直接被嵌入在路径中,URL完整的请求路径,包括了?后面的部分,因此你可以手动解析后面的内容作为GET的参数,Nodejs的url模块中的parse函数提供了这个功能。
const http = require('http');
const url = require('url');
const util = require('util');
http
.createServer((req, res) => {
//利用url模块去解析客户端发送过来的URL
res.write(util.inspect(url.parse(req.url, true)));
res.end();
})
.listen(8080);
获得POST请求内容
POST请求的内容全部都在请求体中,·http.ServerRequest·并没有一个属性内容为请求体,原因是等待请求体传输可能是一件耗时的工作。譬如上传文件。恶意的POST请求会大大消耗服务器的资源。所以Nodejs是不会解析请求体,当你需要的时候,需要手动来做。
简单的看一下代码:
// 获取post请求数据
const http = require('http');
const util = require('util');
const querystring = require('querystring');
http
.createServer((req, res) => {
let post = '';
req.on('data', chunk => {
post += chunk;
});
req.on('end', () => {
post = querystring.parse(post);
res.end(util.inspect(post));
});
})
.listen(60004);
http.ServerResponse返回客户端信息
http.ServerResponse
这个类实现了(而不是继承自)可写流 接口。继承了EventEmitter
。它用来给用户发送响应结果,它是由http.Server
的request
事件发送的,作为第二个参数传递。一般为response或res
主要的三个函数:
response.writeHead(statusCode,[headers])
:向请求的客户端发送响应头。statusCode
是HTTP的状态码,如200为成功,404未找到等。headers
是一个类似关联数组的对象,表示响应头的每个属性。
response.write(data,[encoding])
向请求客户端发送相应内容,data是buffer或字符串,encoding为编码response.end([data],[encoding])
结束响应,告知用户所有发送已经完成,当所有要返回的内容发送完毕,该函数必须被调用一次,如果不调用,客户端永远处于等待状态
总结
真正开发环境,不会用这么底层的API去做web网站或者微服务,一般会选择KOA或者EXPRESS等框架。不过,通过底层的API也可以感受一下NODE原生的开发的快乐。
参考:
1.https://blog.csdn.net/woshinannan741/article/details/51357464
1.http://nodejs.cn/api/http.html#http_http_createserver_requestlistener
github地址:https://github.com/malun666/aicoder_node
Node入门教程(12)第十章:Node的HTTP模块的更多相关文章
- Node入门教程(8)第六章:path 模块详解
path 模块详解 path 模块提供了一些工具函数,用于处理文件与目录的路径.由于windows和其他系统之间路径不统一,path模块还专门做了相关处理,屏蔽了彼此之间的差异. 可移植操作系统接口( ...
- Node入门教程(2)第一章:NodeJS 概述
Node 概述 什么是 Node Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine. Node.js us ...
- Node入门教程(1)目录
aicoder.com 全栈实习之简明 Node 入门文档 aicoder.com 线下实习: 不 8000 就业,不还实习费. 如果需要转载本文档,请联系老马,Q: 515154084 JS基础教程 ...
- Node入门教程(6)第五章:node 模块化(上)模块化演进
node 模块化 JS 诞生的时候,仅仅是为了实现网页表单的本地校验和简单的 dom 操作处理.所以并没有模块化的规范设计. 项目小的时候,我们可以通过命名空间.局部作用域.自执行函数等手段实现变量不 ...
- 《Node入门》读书笔记——用Node.js开发一个小应用
Android APP的开发告一段落,一个稳定的.实现了基本功能的APP已经交付用户使用了!我和老板交流了下,接下来准备转战Node.js了,而且一部分前端的功能也要做进去!哈哈哈~~~接下来要朝一个 ...
- testng入门教程12 TestNG执行多线程测试
testng入门教程 TestNG执行多线程测试 testng入门教程 TestNG执行多线程测试 并行(多线程)技术在软件术语里被定义为软件.操作系统或者程序可以并行地执行另外一段程序中多个部分或者 ...
- ABP入门教程12 - 展示层实现增删改查-脚本
点这里进入ABP入门教程目录 创建目录 在展示层(即JD.CRS.Web.Mvc)的\wwwroot\view-resources\Views\下新建文件夹Course //用以存放Course相关脚 ...
- Node入门教程(7)第五章:node 模块化(下) npm与yarn详解
Node的包管理器 JavaScript缺少包结构的定义,而CommonJS定义了一系列的规范.而NPM的出现则是为了在CommonJS规范的基础上,实现解决包的安装卸载,依赖管理,版本管理等问题. ...
- Node入门教程(5)第四章:global 全局变量
global - 全局变量 全局对象(global object),不要和 全局的对象( global objects )或称标准内置对象混淆.这里说的全局的对象是说在全局作用域里的内的对象.全局作用 ...
随机推荐
- Spring Boot 项目实战(三)集成 Swagger 及 JavaMelody
一.前言 上篇介绍了 Logback 的集成过程,总体已经达到了基本可用的项目结构.本篇主要介绍两个常用工具,接口文档工具 Swagger .项目监控工具 JavaMelody 的集成步骤. 二.Sw ...
- 001.网络TCP/IP工程知识点
一 互联网概述 计算机网络定义:由自主计算机互连起来的集合体. 计算机网络两大部分:硬件:计算机.通信设备.接口设备和传输介质. 软件:通信协议和应用软件. 广域网拓扑结构通常有:网状拓扑结构和环形拓 ...
- 【随笔】借鉴 & KPI式设计
1. 别人(某成功案例)是这么做的,我们也就这么做吧 刚来组里一会就目睹了需求讨论会上的一场争执,大概就是某产品经理在解释需求解释到后面有些说不通了就说“xxx App是这么做的我觉得我们也可以这样做 ...
- Java 8新特性----Lambda
Lambda 一.如何辨别Lambda表达式 Runnable noArguments = () -> System.out.println("Hello World"); ...
- ECS之旅——常用的linux指令
玩过Linux的人都会知道,Linux中的命令的确是非常多,但是玩过Linux的人也从来不会因为Linux的命令如此之多而烦恼,因为我们只需要掌握我们最常用的命令就可以了.当然你也可以在使用时去找一下 ...
- Struts2标签里面调用java方法
<s:if test="#session.user.hasPrivilegeByName(name)"> hasPrivilegeByName(name) 为User类 ...
- angular2项目关于Echarts图表的处理
在NiceFish项目中关于Echarts组件和指令是直接注册在appModule根模块中的,这样路由只需这样写就可以让浏览器加载图标组件: 在chart组件视图中: <div class=&q ...
- db2 OLAP函数使用
说起 DB2 在线分析处理,可以用很好很强大来形容.这项功能特别适用于各种统计查询,这些查询用通常的SQL很难实现,或者根本就无发实现.首先,我们从一个简单的例子开始,来一步一步揭开它神秘的面纱,请看 ...
- 查询返回JSON数据结果集
查询返回JSON数据结果集 设计目标: 1)一次性可以返回N个数据表的JSON数据 2)跨数据库引擎 { "tables": [ { "cols": [ { & ...
- error LNK1104:无法打开文件"lua51.lib"
今天学习C++与Lua通信,遇到了问题:fatal error LNK1104: 无法打开文件“lua51.lib” 开发环境: VS2012 cocos版本:cocos2d-x-3.0 已经按书&l ...