你的浏览器不支持canvas

Love You Ten Thousand Years

Node内置HTTP模块

Date: Author: M/J

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。转载请注明来自小可嗒嗒的博客

HTTPNode的一个核心模块,同时也是一些很底层的API

Node有为数不多的内置模块,HTTP是其中之一,fs也是。这些内置模块你通常可以在Node官方网站的DOCS获取并了解他们的使用方法和APIHTTP的相关API放在了这里:https://nodejs.org/dist/latest-v6.x/docs/api/http.html

这是v6.11.0的版本,就是写博客的最近版本。可以在官网上获取到最新版本。其中标记为Stability:2 - Stable的表示为可放心食用的API,标记为Stability:1 - Experimental的表示为试验性质的API,标记为Stability:0 - Deprecated的表示为被弃用的API

在文档中很简要地介绍了一句:

To use the HTTP server and client one must require('http').

基础

  1. 引入http模块。
var http = require('http');
  1. 创建HTTP服务器

创建HTTP服务器要调用http.createServer()函数。它只有一个回调函数,服务器每次收到HTTP请求后都会调用这个回调函数。

这个请求回调会收到两个参数,请求对象 – req,响应对象 – res

var http = require('http');
var server = http.createServer (function (req,res){
    // 处理请求
});

服务器每收到一条HTTP请求,都会用新的reqres对象触发回调函数。注意,在触发回调函数之前,Node会解析请求的HTTP头,并将他们作为req对象的一部分提供给回调函数。但Node不会在回调函数被触发之前对请求体的解析,HTTP服务器解析完HTTP头,就将控制权转交给回调函数。

  1. 处理请求

你可以按照自己的需求处理请求,我们这里首先调用res.write()方法,将会响应一个数据块,然后用res.end()方法来结束这个响应。

var http = require('http');
var server = http.createServer (function (req,res){
    res.write ('Hello World');
    res.end ();
});

res.write ()res.end ()也能简写成res.end('Hello World')

res.write ()res.end ()之前,还可以对HTTP的响应头部字段进行修改。方法有res.setHeader(field,value)res.getHeader(field)res.removeHeader(field)。上面的例子中,我们使用了默认的状态码200和默认的响应头。

var body = 'Hello World';
res.setHeader ('Content-Length', body.length);
res.setHeader ('Content-Type', 'text/plain');
res.statusCode = 200;
res.end(body);
  1. 绑定一个端口
var http = require('http');
var server = http.createServer (function (req,res){
    res.write ('Hello World');
    res.end ();
});
server.listen(3000);

实例

RESTful Web服务

REST(Representational State Transfer)是一种使用HTTP谓词提供精简的API服务。按照惯例,HTTP谓词GETPOSTPUTDELETE,分别根由URL指定的资源的获取、创建、更新和移除相对应。

具体的RESTful服务,我建议参考StackOverflow关于这个的问答: https://stackoverflow.com/questions/671118/what-exactly-is-restful-programming

其次,我们在这里使用到了cURL这个工具,代替Web浏览器来于Web服务器交互。

cURL的官网在这里: https://curl.haxx.se/github的地址: https://github.com/curl/curl。官网有关于cURL的全部教程。

cRUL是个很棒的工具,自己也只是了解一点点。比如我们输入: curl -I http://xiaokedada.com, 则我们得到:

HTTP/1.1 200 OK
Date: Thu, 08 Jun 2017 02:08:09 GMT
Server: Apache/2.4.23
Last-Modified: Wed, 07 Jun 2017 14:01:00 GMT
ETag: "13c00c3-8e4d-5515f2a869975"
Accept-Ranges: bytes
Content-Length: 36429
Vary: Accept-Encoding,User-Agent
Content-Type: text/html

更多有关cURL工具待后续了解,那个时候,也会另开一篇文章吧。还要介绍的一点就是有关cURL的安装。我们在下载页根据系统找到对应的版本,比如我下载的就是Win64 x86_64 CAB版本,然后在解压到本地文件夹,在1386文件夹中找到CURL.exe,将它拷贝到Windows/System32文件中。

现在,我们RESTful的例子如下:

var http = require('http'),
  url = require('url'),
  items = []

var server = http.createServer(function (req, res) {
  switch (req.method) {
    case 'POST':    //请求资源
      var item = ''
      req.setEncoding('utf8')   //对于文本格式的待办事项而言,我们不需要二进制,将它设定为utf8
      req.on('data', function (chunk) { //只要读入新的utf8字符串,就能触发data事件
        item += chunk
      })
      req.on('end', function () {
        items.push(item)
        res.end('OK\n')
      })
      break
    case 'GET':     //获取资源
      items.forEach(function (item, i) {
        res.write(i + ')' + item + '\n')
      })
      res.end()
      break
    case 'DELETE':
      var path = url.parse(req.url).pathname
      var i = parseInt(path.slice(1), 10)

      if (isNaN(i)) {
        res.statusCode = 404
        res.end('Invalid item id')
      } else if (!items[i]) {
        res.end('Item not found')
      } else {
        items.splice(i, 1)
        res.end('OK\n')
      }
      break
  }
});
server.listen(3000);

特别注意的是,url模块可以的parse()可以解析出pathname。让我们在NodeREPL( Read–Eval–Print Loop)环境,也就是我们的命令行环境验证以下,还是以本博客首页为例。

> require('url').parse('http://xiaokdedada.com');
Url {
  protocol: 'http:',
  slashes: true,
  auth: null,
  host: 'xiaokdedada.com',
  port: null,
  hostname: 'xiaokdedada.com',
  hash: null,
  search: null,
  query: null,
  pathname: '/',
  path: '/',
  href: 'http://xiaokdedada.com/' }

为了简单,我们的RESTful省略了PUT谓词。我们来验证我们的RESTful服务器。首先,我们运行我们的.js文件,开启3000端口。然后,再开一个命令行:

$ curl -d "Hello World" http://localhost:3000
OK
$ curl -d "I am timi" http://localhost:3000
OK
$ curl http://localhost:3000
0) Hello World
1) I am timi

很棒是不是?

提供文件服务

文件服务通常涉及到的是文件的读写,在程序中,我们通常用的是管道(pipe)。

var stream = fs.createReadStream(path);
stream.pipe(res); //res是响应,res.end()会在stream.pipe()内部调用

Node中,所有继承了EventEmitter的类都有可能有error事件。对于fs.ReadStream这样的专用EventEmitter,有预先定义的dataend事件。

从表单中接受用户输入

我们看这个例子:

var http = require('http'),
    qs = require('querystring'),
    items = [];

var server = http.createServer (function(req,res){
    if ('/' == req.url){        //所有不是"/"的URL都会得到一个404 Not Found
        switch (req.method){
            case 'GET':
                show(res);
                break;
            case 'POST':
                add(req,res);
                break;
            default:
                badRequest(res);
        }
    } else {
            notFound(res);
        }
});

server.listen(3000);

function show (res){
    var html = '<html><head><title>Todo List</title></head><body>' + '<h1>Todo List</h1>' + '<ul>' + items.map (function(item){
        return '<li>' + item + '</li>'
    }).join('') + '</ul>' + '<form method="post" action="/">' + '<p><input type="text" name="item" /></p>' + '<p><input type="submit" value="Add Item" /></p>' + '</form></body></html>';
    res.setHeader('Content-Type','text/html');
    res.setHeader('Content-Length', Buffer.byteLength(html));
    res.end (html);
}

function notFound (res){
    res.statusCode = 404;
    res.setHeader ('Content-Type', 'text/plain');
    res.end ('Not Found');
}

function badRequest (res){
    res.statusCode = 400;
    res.setHeader ('Content-Type', 'text/plain');
    res.end ('Bad Request');
}

function add (req, res){
    var body = '';
    req.setEncoding ('utf8');
    req.on ('data', function(chunk){body += chunk});
    req.on ('end', function(){
        var obj = qs.parse(body);
        items.push (obj.item);
        show (res);
    });
}

add()函数解析请求实体时,我们用到了Nodequerystring模块。官方文档中是这样介绍的:

The querystring module provides utilities for parsing and formatting URL query strings.

看一下我们在REPL上的解析。

> var qs = require('querystring');var body = 'item=timi+I+love+you';qs.parse(body);
{ item: 'timi I love you' }

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。