NodeJS 網(wǎng)絡(luò)操作API

2018-09-28 20:37 更新

API 走馬觀花

我們先大致看看 NodeJS 提供了哪些和網(wǎng)絡(luò)操作有關(guān)的 API。這里并不逐一介紹每個(gè)API的使用方法,官方文檔已經(jīng)做得很好了。

HTTP

官方文檔: http://nodejs.org/api/http.html

'http'模塊提供兩種使用方式:

  • 作為服務(wù)端使用時(shí),創(chuàng)建一個(gè) HTTP 服務(wù)器,監(jiān)聽 HTTP 客戶端請求并返回響應(yīng)。

  • 作為客戶端使用時(shí),發(fā)起一個(gè) HTTP 客戶端請求,獲取服務(wù)端響應(yīng)。

首先我們來看看服務(wù)端模式下如何工作。如開門紅中的例子所示,首先需要使用.createServer方法創(chuàng)建一個(gè)服務(wù)器,然后調(diào)用.listen方法監(jiān)聽端口。之后,每當(dāng)來了一個(gè)客戶端請求,創(chuàng)建服務(wù)器時(shí)傳入的回調(diào)函數(shù)就被調(diào)用一次??梢钥闯?,這是一種事件機(jī)制。

HTTP 請求本質(zhì)上是一個(gè)數(shù)據(jù)流,由請求頭(headers)和請求體(body)組成。例如以下是一個(gè)完整的HTTP請求數(shù)據(jù)內(nèi)容。

POST / HTTP/1.1
User-Agent: curl/7.26.0
Host: localhost
Accept: */*
Content-Length: 11
Content-Type: application/x-www-form-urlencoded

Hello World

可以看到,空行之上是請求頭,之下是請求體。HTTP 請求在發(fā)送給服務(wù)器時(shí),可以認(rèn)為是按照從頭到尾的順序一個(gè)字節(jié)一個(gè)字節(jié)地以數(shù)據(jù)流方式發(fā)送的。而 http 模塊創(chuàng)建的 HTTP 服務(wù)器在接收到完整的請求頭后,就會調(diào)用回調(diào)函數(shù)。在回調(diào)函數(shù)中,除了可以使用 request 對象訪問請求頭數(shù)據(jù)外,還能把 request 對象當(dāng)作一個(gè)只讀數(shù)據(jù)流來訪問請求體數(shù)據(jù)。以下是一個(gè)例子。

http.createServer(function (request, response) {
    var body = [];

    console.log(request.method);
    console.log(request.headers);

    request.on('data', function (chunk) {
        body.push(chunk);
    });

    request.on('end', function () {
        body = Buffer.concat(body);
        console.log(body.toString());
    });
}).listen(80);

------------------------------------
POST
{ 'user-agent': 'curl/7.26.0',
  host: 'localhost',
  accept: '*/*',
  'content-length': '11',
  'content-type': 'application/x-www-form-urlencoded' }
Hello World

HTTP 響應(yīng)本質(zhì)上也是一個(gè)數(shù)據(jù)流,同樣由響應(yīng)頭(headers)和響應(yīng)體(body)組成。例如以下是一個(gè)完整的 HTTP 請求數(shù)據(jù)內(nèi)容。

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 11
Date: Tue, 05 Nov 2013 05:31:38 GMT
Connection: keep-alive

Hello World

在回調(diào)函數(shù)中,除了可以使用 response 對象來寫入響應(yīng)頭數(shù)據(jù)外,還能把 response 對象當(dāng)作一個(gè)只寫數(shù)據(jù)流來寫入響應(yīng)體數(shù)據(jù)。例如在以下例子中,服務(wù)端原樣將客戶端請求的請求體數(shù)據(jù)返回給客戶端。

http.createServer(function (request, response) {
    response.writeHead(200, { 'Content-Type': 'text/plain' });

    request.on('data', function (chunk) {
        response.write(chunk);
    });

    request.on('end', function () {
        response.end();
    });
}).listen(80);

接下來我們看看客戶端模式下如何工作。為了發(fā)起一個(gè)客戶端HTTP請求,我們需要指定目標(biāo)服務(wù)器的位置并發(fā)送請求頭和請求體,以下示例演示了具體做法。

var options = {
        hostname: 'www.example.com',
        port: 80,
        path: '/upload',
        method: 'POST',
        headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
        }
    };

var request = http.request(options, function (response) {});

request.write('Hello World');
request.end();

可以看到,.request方法創(chuàng)建了一個(gè)客戶端,并指定請求目標(biāo)和請求頭數(shù)據(jù)。之后,就可以把 request 對象當(dāng)作一個(gè)只寫數(shù)據(jù)流來寫入請求體數(shù)據(jù)和結(jié)束請求。另外,由于 HTTP 請求中 GET 請求是最常見的一種,并且不需要請求體,因此 http 模塊也提供了以下便捷 API。

http.get('http://www.example.com/', function (response) {});

當(dāng)客戶端發(fā)送請求并接收到完整的服務(wù)端響應(yīng)頭時(shí),就會調(diào)用回調(diào)函數(shù)。在回調(diào)函數(shù)中,除了可以使用 response 對象訪問響應(yīng)頭數(shù)據(jù)外,還能把 response 對象當(dāng)作一個(gè)只讀數(shù)據(jù)流來訪問響應(yīng)體數(shù)據(jù)。以下是一個(gè)例子。

http.get('http://www.example.com/', function (response) {
    var body = [];

    console.log(response.statusCode);
    console.log(response.headers);

    response.on('data', function (chunk) {
        body.push(chunk);
    });

    response.on('end', function () {
        body = Buffer.concat(body);
        console.log(body.toString());
    });
});

------------------------------------
200
{ 'content-type': 'text/html',
  server: 'Apache',
  'content-length': '801',
  date: 'Tue, 05 Nov 2013 06:08:41 GMT',
  connection: 'keep-alive' }
<!DOCTYPE html>
...

HTTPS

官方文檔: http://nodejs.org/api/https.html

https 模塊與 http 模塊極為類似,區(qū)別在于 https 模塊需要額外處理 SSL 證書。

在服務(wù)端模式下,創(chuàng)建一個(gè) HTTPS 服務(wù)器的示例如下。

var options = {
        key: fs.readFileSync('./ssl/default.key'),
        cert: fs.readFileSync('./ssl/default.cer')
    };

var server = https.createServer(options, function (request, response) {
        // ...
    });

可以看到,與創(chuàng)建 HTTP 服務(wù)器相比,多了一個(gè) options 對象,通過 key 和 cert 字段指定了 HTTPS 服務(wù)器使用的私鑰和公鑰。

另外,NodeJS 支持 SNI 技術(shù),可以根據(jù) HTTPS 客戶端請求使用的域名動態(tài)使用不同的證書,因此同一個(gè) HTTPS 服務(wù)器可以使用多個(gè)域名提供服務(wù)。接著上例,可以使用以下方法為 HTTPS 服務(wù)器添加多組證書。

server.addContext('foo.com', {
    key: fs.readFileSync('./ssl/foo.com.key'),
    cert: fs.readFileSync('./ssl/foo.com.cer')
});

server.addContext('bar.com', {
    key: fs.readFileSync('./ssl/bar.com.key'),
    cert: fs.readFileSync('./ssl/bar.com.cer')
});

在客戶端模式下,發(fā)起一個(gè) HTTPS 客戶端請求與 http 模塊幾乎相同,示例如下。

var options = {
        hostname: 'www.example.com',
        port: 443,
        path: '/',
        method: 'GET'
    };

var request = https.request(options, function (response) {});

request.end();

但如果目標(biāo)服務(wù)器使用的 SSL 證書是自制的,不是從頒發(fā)機(jī)構(gòu)購買的,默認(rèn)情況下 https 模塊會拒絕連接,提示說有證書安全問題。在 options 里加入 rejectUnauthorized: false 字段可以禁用對證書有效性的檢查,從而允許 https 模塊請求開發(fā)環(huán)境下使用自制證書的 HTTPS 服務(wù)器。

URL

官方文檔: http://nodejs.org/api/url.html

處理 HTTP 請求時(shí) url 模塊使用率超高,因?yàn)樵撃K允許解析 URL、生成 URL,以及拼接 URL。首先我們來看看一個(gè)完整的 URL 的各組成部分。


                           href
 -----------------------------------------------------------------
                            host              path
                      --------------- ----------------------------
 http: // user:pass @ host.com : 8080 /p/a/t/h ?query=string #hash
 -----    ---------   --------   ---- -------- ------------- -----
protocol     auth     hostname   port pathname     search     hash
                                                ------------
                                                   query

我們可以使用.parse方法來將一個(gè) URL 字符串轉(zhuǎn)換為 URL 對象,示例如下。

url.parse('http://user:pass@host.com:8080/p/a/t/h?query=string#hash');
/* =>
{ protocol: 'http:',
  auth: 'user:pass',
  host: 'host.com:8080',
  port: '8080',
  hostname: 'host.com',
  hash: '#hash',
  search: '?query=string',
  query: 'query=string',
  pathname: '/p/a/t/h',
  path: '/p/a/t/h?query=string',
  href: 'http://user:pass@host.com:8080/p/a/t/h?query=string#hash' }
*/

傳給.parse方法的不一定要是一個(gè)完整的 URL,例如在 HTTP 服務(wù)器回調(diào)函數(shù)中,request.url 不包含協(xié)議頭和域名,但同樣可以用.parse方法解析。

http.createServer(function (request, response) {
    var tmp = request.url; // => "/foo/bar?a=b"
    url.parse(tmp);
    /* =>
    { protocol: null,
      slashes: null,
      auth: null,
      host: null,
      port: null,
      hostname: null,
      hash: null,
      search: '?a=b',
      query: 'a=b',
      pathname: '/foo/bar',
      path: '/foo/bar?a=b',
      href: '/foo/bar?a=b' }
    */
}).listen(80);

.parse方法還支持第二個(gè)和第三個(gè)布爾類型可選參數(shù)。第二個(gè)參數(shù)等于 true 時(shí),該方法返回的 URL 對象中,query 字段不再是一個(gè)字符串,而是一個(gè)經(jīng)過 querystring 模塊轉(zhuǎn)換后的參數(shù)對象。第三個(gè)參數(shù)等于 true 時(shí),該方法可以正確解析不帶協(xié)議頭的 URL,例如//www.example.com/foo/bar

反過來,format 方法允許將一個(gè) URL 對象轉(zhuǎn)換為 URL 字符串,示例如下。

url.format({
    protocol: 'http:',
    host: 'www.example.com',
    pathname: '/p/a/t/h',
    search: 'query=string'
});
/* =>
'http://www.example.com/p/a/t/h?query=string'
*/

另外,.resolve方法可以用于拼接 URL,示例如下。

url.resolve('http://www.example.com/foo/bar', '../baz');
/* =>
http://www.example.com/baz
*/

Query String

官方文檔: http://nodejs.org/api/querystring.html

querystring 模塊用于實(shí)現(xiàn) URL 參數(shù)字符串與參數(shù)對象的互相轉(zhuǎn)換,示例如下。

querystring.parse('foo=bar&baz=qux&baz=quux&corge');
/* =>
{ foo: 'bar', baz: ['qux', 'quux'], corge: '' }
*/

querystring.stringify({ foo: 'bar', baz: ['qux', 'quux'], corge: '' });
/* =>
'foo=bar&baz=qux&baz=quux&corge='
*/

Zlib

官方文檔: http://nodejs.org/api/zlib.html

zlib 模塊提供了數(shù)據(jù)壓縮和解壓的功能。當(dāng)我們處理 HTTP 請求和響應(yīng)時(shí),可能需要用到這個(gè)模塊。

首先我們看一個(gè)使用 zlib 模塊壓縮 HTTP 響應(yīng)體數(shù)據(jù)的例子。這個(gè)例子中,判斷了客戶端是否支持 gzip,并在支持的情況下使用 zlib 模塊返回 gzip 之后的響應(yīng)體數(shù)據(jù)。

http.createServer(function (request, response) {
    var i = 1024,
        data = '';

    while (i--) {
        data += '.';
    }

    if ((request.headers['accept-encoding'] || '').indexOf('gzip') !== -1) {
        zlib.gzip(data, function (err, data) {
            response.writeHead(200, {
                'Content-Type': 'text/plain',
                'Content-Encoding': 'gzip'
            });
            response.end(data);
        });
    } else {
        response.writeHead(200, {
            'Content-Type': 'text/plain'
        });
        response.end(data);
    }
}).listen(80);

接著我們看一個(gè)使用 zlib 模塊解壓 HTTP 響應(yīng)體數(shù)據(jù)的例子。這個(gè)例子中,判斷了服務(wù)端響應(yīng)是否使用 gzip 壓縮,并在壓縮的情況下使用 zlib 模塊解壓響應(yīng)體數(shù)據(jù)。

var options = {
        hostname: 'www.example.com',
        port: 80,
        path: '/',
        method: 'GET',
        headers: {
            'Accept-Encoding': 'gzip, deflate'
        }
    };

http.request(options, function (response) {
    var body = [];

    response.on('data', function (chunk) {
        body.push(chunk);
    });

    response.on('end', function () {
        body = Buffer.concat(body);

        if (response.headers['content-encoding'] === 'gzip') {
            zlib.gunzip(body, function (err, data) {
                console.log(data.toString());
            });
        } else {
            console.log(data.toString());
        }
    });
}).end();

Net

官方文檔: http://nodejs.org/api/net.html

net 模塊可用于創(chuàng)建 Socket 服務(wù)器或 Socket 客戶端。由于 Socket 在前端領(lǐng)域的使用范圍還不是很廣,這里先不涉及到 WebSocket 的介紹,僅僅簡單演示一下如何從 Socket 層面來實(shí)現(xiàn) HTTP 請求和響應(yīng)。

首先我們來看一個(gè)使用 Socket 搭建一個(gè)很不嚴(yán)謹(jǐn)?shù)?HTTP 服務(wù)器的例子。這個(gè) HTTP 服務(wù)器不管收到啥請求,都固定返回相同的響應(yīng)。

net.createServer(function (conn) {
    conn.on('data', function (data) {
        conn.write([
            'HTTP/1.1 200 OK',
            'Content-Type: text/plain',
            'Content-Length: 11',
            '',
            'Hello World'
        ].join('\n'));
    });
}).listen(80);

接著我們來看一個(gè)使用 Socket 發(fā)起 HTTP 客戶端請求的例子。這個(gè)例子中,Socket 客戶端在建立連接后發(fā)送了一個(gè) HTTP GET 請求,并通過 data 事件監(jiān)聽函數(shù)來獲取服務(wù)器響應(yīng)。

var options = {
        port: 80,
        host: 'www.example.com'
    };

var client = net.connect(options, function () {
        client.write([
            'GET / HTTP/1.1',
            'User-Agent: curl/7.26.0',
            'Host: www.baidu.com',
            'Accept: */*',
            '',
            ''
        ].join('\n'));
    });

client.on('data', function (data) {
    console.log(data.toString());
    client.end();
});
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號