zoukankan      html  css  js  c++  java
  • 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:

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

    相关链接

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

  • 相关阅读:
    actionscript3.0 图片裁剪及保存jpg详解
    AS3 JPEG Encoder应用:从Flash中保存图片
    Kata 架构
    docker 学习笔记
    processon
    学习容器技术的思考
    cp -f 还是提示是否覆盖
    CentOS下安装桌面环境
    [PYTHON 实作] 算100
    <转>CentOS 7 安装配置 NFS
  • 原文地址:https://www.cnblogs.com/eret9616/p/9187573.html
Copyright © 2011-2022 走看看