NodeJS学习笔记 (5)网络服务-http-req(ok)
原文:https://github.com/chyingp/nodejs-learning-guide
自己敲代码:
概览
本文的重点会放在req
这个对象上。前面已经提到,它其实是http.IncomingMessage实例,在服务端、客户端作用略微有差异
- 服务端处:获取请求方的相关信息,如request header等。
- 客户端处:获取响应方返回的相关信息,如statusCode等。
服务端例子:
// 下面的 req
var http = require('http');
var server = http.createServer(function(req, res){
console.log(req.headers);
res.end('ok');
});
server.listen(3000);
客户端例子
// 下面的res
var http = require('http');
http.get('http://127.0.0.1:3000', function(res){
console.log(res.statusCode);
});
属性/方法/事件 分类
http.IncomingMessage的属性/方法/事件 不是特别多,按照是否客户端/服务端 特有的,下面进行简单归类。可以看到
- 服务端处特有:url
- 客户端处特有:statusCode、statusMessage
类型 | 名称 | 服务端 | 客户端 |
---|---|---|---|
事件 | aborted | ✓ | ✓ |
事件 | close | ✓ | ✓ |
属性 | headers | ✓ | ✓ |
属性 | rawHeaders | ✓ | ✓ |
属性 | statusCode | ✕ | ✓ |
属性 | statusMessage | ✕ | ✓ |
属性 | httpVersion | ✓ | ✓ |
属性 | httpVersion | ✓ | ✓ |
属性 | url | ✓ | ✕ |
属性 | socket | ✓ | ✓ |
方法 | .destroy() | ✓ | ✓ |
方法 | .setTimeout() | ✓ |
✓ |
服务端的例子
例子一:获取httpVersion/method/url
下面是一个典型的HTTP请求报文,里面最重要的内容包括:HTTP版本、请求方法、请求地址、请求头部。
GET /hello HTTP/1.1
Host: 127.0.0.1:3000
Connection: keep-alive
Cache-Control: no-cache
那么,如何获取上面提到的信息呢?很简单,直接上代码
// getClientInfo.js
var http = require('http'); var server = http.createServer(function(req, res){
console.log( '1、客户端请求url:' + req.url );
console.log( '2、http版本:' + req.httpVersion );
console.log( '3、http请求方法:' + req.method );
console.log( '4、http请求头部' + JSON.stringify(req.headers) ); res.end('ok');
}); server.listen(3000);
效果如下:
1、客户端请求url:/hello
2、http版本:1.1
3、http请求方法:GET
4、http headers:{"host":"127.0.0.1:3000","connection":"keep-alive","cache-control":"no-cache","user-agent":"Mozilla/5.0
例子二:获取get请求参数
服务端代码如下:
// getClientGetQuery.js
var http = require('http');
var url = require('url');
var querystring = require('querystring'); var server = http.createServer(function(req, res){
var urlObj = url.parse(req.url);
var query = urlObj.query;
var queryObj = querystring.parse(query); console.log( JSON.stringify(queryObj) ); res.end('ok');
}); server.listen(3000);
访问地址 http://127.0.0.1:3000/hello?nick=chyingp&hello=world
服务端输出如下
{"nick":"chyingp","hello":"world"}
例子三:获取post请求参数
服务端代码如下
// getClientPostBody.js
var http = require('http');
var url = require('url');
var querystring = require('querystring'); var server = http.createServer(function(req, res){ var body = '';
req.on('data', function(thunk){
body += thunk;
}); req.on('end', function(){
console.log( 'post body is: ' + body );
res.end('ok');
});
}); server.listen(3000);
通过curl构造post请求:
curl -d 'nick=casper&hello=world' http://127.0.0.1:3000
服务端打印如下:
post body is: nick=casper&hello=world
备注:post请求中,不同的Content-type
,post body有不小差异,感兴趣的同学可以研究下。
本例中的post请求,HTTP报文大概如下
POST / HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache nick=casper&hello=world
客户端处例子
例子一:获取httpVersion/statusCode/statusMessage
代码如下:
var http = require('http');
var server = http.createServer(function(req, res){
res.writeHead(200, {'content-type': 'text/plain',});
res.end('from server');
});
server.listen(3000); var client = http.get('http://127.0.0.1:3000', function(res){
console.log('1、http版本:' + res.httpVersion);
console.log('2、返回状态码:' + res.statusCode);
console.log('3、返回状态描述信息:' + res.statusMessage);
console.log('4、返回正文:'); res.pipe(process.stdout);
});
控制台输出:
1、http版本:1.1
2、返回状态码:200
3、返回状态描述信息:OK
4、返回正文:
from server
事件对比:aborted、close
官方文档对这两个事件的解释是:当客户端终止请求时,触发aborted事件;当客户端连接断开时,触发close事件;官方文档传送们:地址
解释得比较含糊,从实际实验对比上来看,跟官方文档有不小出入。此外,客户端处、服务端处的表现也是不同的。
服务端表现
根据实际测试结果来看,当客户端:
- abort请求时,服务端req的aborted、close事件都会触发;(诡异)
- 请求正常完成时,服务端req的close事件不会触发;(也很诡异)
直接扒了下nodejs的源代码,发现的确是同时触发的,触发场景:请求正常结束前,客户端abort请求。
测试代码如下:
var http = require('http'); var server = http.createServer(function(req, res){ console.log('1、收到客户端请求: ' + req.url); req.on('aborted', function(){
console.log('2、客户端请求aborted');
}); req.on('close', function(){
console.log('3、客户端请求close');
}); // res.end('ok'); 故意不返回,等着客户端中断请求
}); server.listen(3000, function(){
var client = http.get('http://127.0.0.1:3000/aborted');
setTimeout(function(){
client.abort(); // 故意延迟100ms,确保请求发出
}, 100);
}); // 输出如下
// 1、收到客户端请求: /aborted
// 2、客户端请求aborted
// 3、客户端请求close
以下代码来自nodejs源码(_http_server.js)
function abortIncoming() {
while (incoming.length) {
var req = incoming.shift();
req.emit('aborted');
req.emit('close');
}
// abort socket._httpMessage ?
}
再来一波对比,req.on('close')
和req.socket.on('close')
。
// reqSocketClose.js
var http = require('http'); var server = http.createServer(function(req, res){ console.log('server: 收到客户端请求'); req.on('close', function(){
console.log('server: req close');
}); req.socket.on('close', function(){
console.log('server: req.socket close');
}); res.end('ok');
}); server.listen(3000); var client = http.get('http://127.0.0.1:3000/aborted', function(res){
console.log('client: 收到服务端响应');
});
输出如下,正儿八经的close事件触发了。
server: 收到客户端请求
server: req.socket close
client: 收到服务端响应
客户端表现
猜测客户端的aborted、close也是在类似场景下触发,测试代码如下。发现一个比较有意思的情况,res.pipe(process.stdout)
这行代码是否添加,会影响close
是否触发。
- 没有
res.pipe(process.stdout)
:close不触发。 - 有
res.pipe(process.stdout)
:close触发。
var http = require('http'); var server = http.createServer(function(req, res){ console.log('1、服务端:收到客户端请求'); res.flushHeaders();
res.setTimeout(100); // 故意不返回,3000ms后超时
}); server.on('error', function(){}); server.listen(3000, function(){ var client = http.get('http://127.0.0.1:3000/aborted', function(res){ console.log('2、客户端:收到服务端响应'); // res.pipe(process.stdout); 注意这行代码 res.on('aborted', function(){
console.log('3、客户端:aborted触发!');
}); res.on('close', function(){
console.log('4、客户端:close触发!');
});
});
});
信息量略大的 .destroy()
经过前面aborted、close的摧残,本能的觉得 .destroy() 方法的表现会有很多惊喜之处。
测试代码如下:
var http = require('http'); var server = http.createServer(function(req, res){ console.log('服务端:收到客户端请求'); req.destroy(new Error('测试destroy')); req.on('error', function(error){
console.log('服务端:req error: ' + error.message);
}); req.socket.on('error', function(error){
console.log('服务端:req socket error: ' + error.message);
})
}); server.on('error', function(error){
console.log('服务端:server error: ' + error.message);
}); server.listen(3000, function(){ var client = http.get('http://127.0.0.1:3000/aborted', function(res){
// do nothing
}); client.on('error', function(error){
console.log('客户端:client error触发!' + error.message);
});
});
输出如下。根据 .destroy() 调用的时机不同,error 触发的对象不同。(测试过程比较枯燥,有时间再总结一下)
服务端:收到客户端请求
服务端:req socket error: 测试destroy
客户端:client error触发!socket hang up
不常用属性
- rawHeaders:未解析前的request header。
- trailers:在分块传输编码(chunk)中用到,表示trailer后的header可分块传输。(感兴趣的可以研究下)
- rawTrailers:
关于trailers属性:
The request/response trailers object. Only populated at the 'end' event.
写在后面
一个貌似很简单的对象,实际比想的要复杂一些。做了不少对比实验,也发现了一些好玩的东西,打算深入学习的同学可以自己多动手尝试一下 :)
TODO:
- 对close、aborted进行更深入对比
- 对.destroy()进行更深入对比
相关链接
官方文档: https://nodejs.org/api/http.html#http_class_http_incomingmessage
NodeJS学习笔记 (5)网络服务-http-req(ok)的更多相关文章
- NodeJS学习笔记 (9)网络服务-https(ok)
模块概览 这个模块的重要性,基本不用强调了.在网络安全问题日益严峻的今天,网站采用HTTPS是个必然的趋势. 在nodejs中,提供了 https 这个模块来完成 HTTPS 相关功能.从官方文档来看 ...
- NodeJS学习笔记 (8)网络服务-http-server(ok)
http服务端概览 创建server 几行代码搞定 var http = require('http'); var requestListener = function(req, res){ res. ...
- NodeJS学习笔记 (4)网络服务-http(ok)
原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: http模块概览 大多数nodejs开发者都是冲着开发web server的目的选 ...
- NodeJS学习笔记 (7)网络服务-http-client(ok)
原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: ClientRequest概览 当你调用 http.request(options ...
- NodeJS学习笔记 (6)网络服务-http-res(ok)
原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: 概览 http模块四剑客之一的res,应该都不陌生了.一个web服务程序,接受到来 ...
- nodejs学习笔记之网络编程
了解一下OSI七层模型 OSI层 功能 TCP/IP协议 应用层 文件传输,电子邮件,文件服务,虚拟终端 TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层 数据格式化 ...
- NodeJS学习笔记 (10)网络TCP-net(ok)
模块概览 net模块是同样是nodejs的核心模块.在http模块概览里提到,http.Server继承了net.Server,此外,http客户端与http服务端的通信均依赖于socket(net. ...
- NodeJS学习笔记 (12)网络地址解析-url(ok)
模块概述 nodejs中,提供了url这个非常实用的模块,用来做URL的解析.在做node服务端的开发时会经常用到.使用很简单,总共只有3个方法. 正式讲解前,各位同学先把下面这个图记在心上(来自no ...
- NodeJS学习笔记 (11)网络UDP-dgram(ok)
模块概览 dgram模块是对UDP socket的一层封装,相对net模块简单很多,下面看例子. UPD客户端 vs UDP服务端 首先,启动UDP server,监听来自端口33333的请求. se ...
随机推荐
- Book 树状数组 小结
差不多花了10天学树状数组,是照着这篇博客做的题目,还差几道---------- http://blog.csdn.net/chenguolinblog/article/details/9916229 ...
- C++的头文件(转)
这几天在写比较困难的一部分,所以也没有时间总结一些东西了,不过昨天翻我的笔记本,发现了一篇还不错的笔记,给大家看看. C/C++头文件一览 C.传统 C++ #include <assert.h ...
- ABBYY简体中文版终身授权半价来袭,真的是5折!
经过了一个春秋,心心念念的双十一终于要来了,一年时间并不长,但这一个月尤其慢!ABBYY官方称为回馈广大用户的支持与厚爱,双十一期间,ABBYY价格感人,诱惑难挡. 说到双十一活动,方式也是五花八门, ...
- Python多进程原理与实现
Date: 2019-06-04 Author: Sun 1 进程的基本概念 什么是进程? 进程就是一个程序在一个数据集上的一次动态执行过程.进程一般由程序.数据集.进程控制块三部分组成.我们编写 ...
- 用私有构造器或枚举类型强化Singleton
Singleton指只有一个实例的类,只能被创建一次. 在Java1.5之前实现Singleton有两种方式,都是将构造器设为private并导出公有的静态成员实例. 第一种方式将公有的静态成员实例设 ...
- idea--IntelliJ IDEA隐藏不想看到的文件或文件夹
打开IntelliJ IDEA,File -> Settings -> Editor -> File Types 在红框部分加上你想过滤的文件或文件夹名
- Codeforces Round #506 (Div. 3) A-C
CF比赛题解(简单题) 简单题是指自己在比赛期间做出来了 A. Many Equal Substrings 题意 给个字符串t,构造一个字符串s,使得s中t出现k次;s的长度最短 如t="c ...
- XPATH怎么获取TITLE中有中文的标签
定位 //*[@id="kkpager"]/div[1]/span[1]/a[@title="下一页"] 获取元素 txt4 = txt.xpath('//*[ ...
- 小学生都能学会的python(<lamda匿名函数,sorted(),filter(),map(),递归函数>)
小学生都能学会的python(<<lamda匿名函数,sorted(),filter(),map(),递归函数,二分法>> 1. lambda 匿名函数 lambda 参数: ...
- 对象不支持“abigimage”属性或方法
在一个网页中用了一个js插件, js文件引用的没有错,代码也和demo差点儿相同, 可是执行时ie的调试工具报了一个错: 解决方式: jquery文件冲突,发现原来自己引过一个 <script ...