原文: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. dos命令,jdk&jre&jvm的关系,classpath和path区别

    Day01_SHJavaTraing_4-2-2017 一.学习方法 1.独立思考 2.独立整理.总结知识点 3.整理Exception报错文档 4.莫钻牛角尖 5.敲代码,敲,猛敲,死里敲 6.写代 ...

  2. anshi

    env.js主要是帮助我们读取根目录下以.env开头的环境变量,并判断是否生效. 比如在根目录下创建一个.env.local文件 改写一个环境变量 而没有改之前,它是在3000端口打开 path.js ...

  3. 一些css布局

    # css布局 #---bootstrap   详情请看官方文档---首先要按照相应的官方规范引入相应的css js fonts等 container相当于一个容器 一般设置一个 接下来设置行 用ro ...

  4. 线段树合并(【POI2011】ROT-Tree Rotations)

    线段树合并([POI2011]ROT-Tree Rotations) 题意 现在有一棵二叉树,所有非叶子节点都有两个孩子.在每个叶子节点上有一个权值(有nn个叶子节点,满足这些权值为1-n1-n的一个 ...

  5. UNIX系统高级编程——第四章-文件和目录-总结

    文件系统: 以UNIX系统V文件系统为例: 磁盘分为区,每个分区都有自己的文件系统: ​ i节点是固定长度的记录项,包含了文件的相关信息.目录项包含文件名和i节点号.stat结构中除文件名和i节点编号 ...

  6. pytorch 1 torch_numpy, 对比

    import torch import numpy as np details about math operation in torch can be found in: http://pytorc ...

  7. 【转】C语言将字符串转换成对应的数字(十进制、十六进制)

    转自:http://wawlian.iteye.com/blog/1315133 1.一个十进制数字的字符串表示转换成对应的整数.举例:将“1234”转换成整数1234 /*将字符串s转换成相应的整数 ...

  8. strlen()函数对一个未初始化数组的处理

    今天使用strlen时 ,发现一个问题,demo代码如下: #include <stdio.h> #include <stdlib.h> #include <string ...

  9. url中jsessionid的理解

    (1) 这是一个保险措施 因为Session默认是需要Cookie支持的 但有些客户浏览器是关闭Cookie的 这个时候就需要在URL中指定服务器上的session标识,也就是5F4771183629 ...

  10. Myeclipse学习总结(9)——MyEclipse2014安装插件的几种方式(适用于Eclipse或MyEclipse其他版本)

    众所周知MyEclipse是一个很强大的Java IDE,而且它有许多开源免费又好用的插件,这些插件给我们开发过程中带来了许多方便.插件具有针对性,例如,你如果做安卓开发,可能需要一个ADT(Andr ...