原文: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:

  1. 对close、aborted进行更深入对比
  2. 对.destroy()进行更深入对比

相关链接

官方文档: https://nodejs.org/api/http.html#http_class_http_incomingmessage

NodeJS学习笔记 (5)网络服务-http-req(ok)的更多相关文章

  1. NodeJS学习笔记 (9)网络服务-https(ok)

    模块概览 这个模块的重要性,基本不用强调了.在网络安全问题日益严峻的今天,网站采用HTTPS是个必然的趋势. 在nodejs中,提供了 https 这个模块来完成 HTTPS 相关功能.从官方文档来看 ...

  2. NodeJS学习笔记 (8)网络服务-http-server(ok)

    http服务端概览 创建server 几行代码搞定 var http = require('http'); var requestListener = function(req, res){ res. ...

  3. NodeJS学习笔记 (4)网络服务-http(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: http模块概览 大多数nodejs开发者都是冲着开发web server的目的选 ...

  4. NodeJS学习笔记 (7)网络服务-http-client(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: ClientRequest概览 当你调用 http.request(options ...

  5. NodeJS学习笔记 (6)网络服务-http-res(ok)

    原文:https://github.com/chyingp/nodejs-learning-guide 自己敲代码: 概览 http模块四剑客之一的res,应该都不陌生了.一个web服务程序,接受到来 ...

  6. nodejs学习笔记之网络编程

    了解一下OSI七层模型   OSI层 功能 TCP/IP协议 应用层 文件传输,电子邮件,文件服务,虚拟终端  TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 表示层 数据格式化 ...

  7. NodeJS学习笔记 (10)网络TCP-net(ok)

    模块概览 net模块是同样是nodejs的核心模块.在http模块概览里提到,http.Server继承了net.Server,此外,http客户端与http服务端的通信均依赖于socket(net. ...

  8. NodeJS学习笔记 (12)网络地址解析-url(ok)

    模块概述 nodejs中,提供了url这个非常实用的模块,用来做URL的解析.在做node服务端的开发时会经常用到.使用很简单,总共只有3个方法. 正式讲解前,各位同学先把下面这个图记在心上(来自no ...

  9. NodeJS学习笔记 (11)网络UDP-dgram(ok)

    模块概览 dgram模块是对UDP socket的一层封装,相对net模块简单很多,下面看例子. UPD客户端 vs UDP服务端 首先,启动UDP server,监听来自端口33333的请求. se ...

随机推荐

  1. 苹果操作系统名称演变史 新名称macOS

    历史回顾 发布年代 名称 序号 1994-1999 Classic Mac OS 1-9 2001-2011 Mac OS X 10.0-Lion 2012-2015 OS X Mountain Li ...

  2. 使用C++部署Keras或TensorFlow模型

    本文介绍如何在C++环境中部署Keras或TensorFlow模型. 一.对于Keras, 第一步,使用Keras搭建.训练.保存模型. model.save('./your_keras_model. ...

  3. Airtest ——poco

    1.  Pymysql(No module named ‘cryptography’) pip install cryptography pip install paramiko 把 cryptogr ...

  4. js追加元素

    直接运行 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  5. [luogu] P3202 [HNOI2009]通往城堡之路(贪心)

    P3202 [HNOI2009]通往城堡之路 题目描述 听说公主被关押在城堡里,彭大侠下定决心:不管一路上有多少坎坷,不管城堡中的看守有多少厉害,不管救了公主之后公主会不会再被抓走,不管公主是否漂亮. ...

  6. STM32 Cubemx 输出可调频率与占空比的PWM

    这里就不对STM32的PWM进行讲解了,想要了解的可以百度一下,这里主要说怎么实现. 1.建立工程,我选的是STM32F103zet6芯片,选择定时器的PWM功能 2.配置时钟,我这里配的是内部时钟, ...

  7. MD5加密技术

    前几天,在看OpenVXI3.4的时候,偶然发现了几个奇怪的文件,那就是OpenVXI-3.4\src\cache下面的,base64.c,base64.h,md5.c,md5.h.既然有人把源代码给 ...

  8. HDU 4334 Contest 4

    本来以为是一道水题,好吧,做了才知道,出题的人有多牛.二分搜索是不可能的了,因为会超内存... 看到别人的搜索两个集合的提示,我就自己一边去想了.终于想出来了: 可以这样做,先把每两个集合的和值枚举出 ...

  9. Tokyo Tyrant(TTServer)系列(二)-启动參数和配置

    启动參数介绍         ttserver命令能够启动一个数据库实例.由于数据库已经实现了Tokyo Cabinet的抽象API,所以能够在启动的时候指定数据库的配置类型. 支持的数据库类型有: ...

  10. [CSS3] Responsive Table -- no more table

    When the screen size is small, we can use "no more table" solution. So instead of render t ...