1. 程式人生 > >nodejs http模組的講解以及request包的使用

nodejs http模組的講解以及request包的使用

http

大部分的node使用者,都是用node來做Web API的,而HTTP模組是提供Web API的基礎。為了支援所有的HTTP應用,node中的HTTTP模組提供的API是偏向底層化的。利用HTTP模組,我們可以簡單快速搭建一個Web Server。

node提供了http這個核心模組(不用安裝哦,直接require就可以了),用於建立http server服務,使用下面程式碼,輕鬆在本機的3000埠建立一個http伺服器

// http_demo.js
var http=require("http");

http.createServer(function(req,res){
    res.writeHead(200
,{ "content-type":"text/plain" }); res.write("hello world"); res.end(); }).listen(3000); $~ node http_demo.js
  • 流程

    我們首先用http.createServer函式建立了一個伺服器物件,然後呼叫了response.writeHead方法:該方法的第一個引數表示HTTP的響應狀態(200)表示一切正常;第二個引數是“Content-Type”,表示我響應給客戶端的內容型別。然再後我們呼叫了response.write方法,寫入我們需要傳遞給客戶端的內容。最後一步我們呼叫了response.end,表示此次請求已處理完成。

  • .listen(port)

    此函式有兩個引數,第一個引數表示我們需要監聽的埠,第二個引數是回撥函式(其實是listening事件),當監聽開啟後立刻觸發。

下面我們逐步展開HTTP 的 API

httpService (http伺服器)

開篇的例項程式碼,也可以通過如下的程式碼進行改寫一番:

var http=require("http");
var server=new http.Server();

server.on("request",function(req,res){
    res.writeHead(200,{
        "content-type":"text/plain"
    });
    res.write("hello nodejs"
); res.end(); }); server.listen(3000);

以上程式碼是通過直接建立一個http.Server物件,然後為其新增request事件監聽,其實也就說createServer方法其實本質上也是為http.Server物件添加了一個request事件監聽,這似乎更好理解了,那讓我們看看http的重要屬性

createServer方法中的引數函式中的兩個引數req和res則是分別代表了請求物件和響應物件。其中req是http.IncomingMessage的例項,res是http.ServerResponse的例項。

  • http.IncomingMessage

    http.IncomingMessage是HTTP請求的資訊,是後端開發者最關注的內容,一般由http.Server的request事件傳送,並作為第一個引數傳遞,包含三個事件

    • data:當請求體資料到來時,該事件被觸發,該事件提供一個引數chunk,表示接受的資料,如果該事件沒有被監聽,則請求體會被拋棄,該事件可能會被呼叫多次(這與nodejs是非同步的有關係)
    • end:當請求體資料傳輸完畢時,該事件會被觸發,此後不會再有資料
    • close:使用者當前請求結束時,該事件被觸發,不同於end,如果使用者強制終止了傳輸,也是用close
  • http.ServerResponse

    http.ServerResponse是返回給客戶端的資訊,決定了使用者最終看到的內容,一般也由http.Server的request事件傳送,並作為第二個引數傳遞,它有三個重要的成員函式,用於返回響應頭、響應內容以及結束請求

    • res.writeHead(statusCode,[heasers]):向請求的客戶端傳送響應頭,該函式在一個請求中最多呼叫一次,如果不呼叫,則會自動生成一個響應頭
    • res.write(data,[encoding]):想請求的客戶端傳送相應內容,data是一個buffer或者字串,如果data是字串,則需要制定編碼方式,預設為utf-8,在res.end呼叫之前可以多次呼叫
    • res.end([data],[encoding]):結束響應,告知客戶端所有傳送已經結束,當所有要返回的內容傳送完畢時,該函式必需被呼叫一次,兩個可選引數與res.write()相同。如果不呼叫這個函式,客戶端將用於處於等待狀態。

http client

http模組提供了兩個函式 http.requesthttp.get,功能是作為客戶端向http伺服器發起請求。

  • http.request(options,callback)

    options是一個類似關聯陣列的物件,表示請求的引數,callback作為回撥函式,需要傳遞一個引數,為http.ClientResponse的例項,http.request返回一個http.ClientRequest的例項。

    options常用的引數有host、port(預設為80)、method(預設為GET)、path(請求的相對於根的路徑,預設是“/”,其中querystring應該包含在其中,例如/search?query=byvoid)、headers(請求頭內容)
    var http=require("http");

      var options={
          hostname:"cn.bing.com",
          port:8080
      }
      
      var req=http.request(options,function(res){
          res.setEncoding("utf-8");
          res.on("data",function(chunk){
              console.log(chunk.toString())
          });
          console.log(res.statusCode);
      });
      req.on("error",function(err){
          console.log(err.message);
      });
      req.end();
    

    傳送POST請求(模擬了嚮慕課網發起評論的功能,headers請使用開發者工具從請求中獲取,基本上是參考scott老師的程式碼)

      var http=require("http");
      var querystring=require("querystring");
      
      var postData=querystring.stringify({
          "content":"just a test",
          "mid":8837
      });
      
      var options={
          hostname:"www.imooc.com",
          port:80,
          path:"/course/document",
          method:"POST",
          headers:{
              "Accept":"application/json, text/javascript, */*; q=0.01",
              "Accept-Encoding":"gzip, deflate",
              "Accept-Language":"zh-CN,zh;q=0.8",
              "Connection":"keep-alive",
              "Content-Length":postData.length,
              "Content-Type":"application/x-www-form-urlencoded; charset=UTF-8",
              "Cookie":"imooc_uuid=6cc9e8d5-424a-4861-9f7d-9cbcfbe4c6ae; imooc_isnew_ct=1460873157; loginstate=1; apsid=IzZDJiMGU0OTMyNTE0ZGFhZDAzZDNhZTAyZDg2ZmQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjkyOTk0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGNmNmFhMmVhMTYwNzRmMjczNjdmZWUyNDg1ZTZkMGM1BwhXVwcIV1c%3DMD; PHPSESSID=thh4bfrl1t7qre9tr56m32tbv0; Hm_lvt_f0cfcccd7b1393990c78efdeebff3968=1467635471,1467653719,1467654690,1467654957; Hm_lpvt_f0cfcccd7b1393990c78efdeebff3968=1467655022; imooc_isnew=2; cvde=577a9e57ce250-34",
              "Host":"www.imooc.com",
              "Origin":"http://www.imooc.com",
              "Referer":"http://www.imooc.com/video/8837",
              "User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2763.0 Safari/537.36",
              "X-Requested-With":"XMLHttpRequest",
          }
      }
      
      var req=http.request(options,function(res){
          res.on("data",function(chunk){
              console.log(chunk);
          });
          res.on("end",function(){
              console.log("### end ##");
          });
          console.log(res.statusCode);
      });
      
      req.on("error",function(err){
          console.log(err.message);
      })
      req.write(postData);
      req.end();
    
  • http.get(options,callback)

    這個方法是http.request方法的簡化版,唯一的區別是http.get自動將請求方法設為了GET請求,同時不需要手動呼叫req.end(),但是需要記住的是,如果我們使用http.request方法時沒有呼叫end方法,伺服器將不會收到資訊。

request

可以將request模組想象成一個簡化版的第三方類http模組,同時支援https 和重定向,戳這裡區官網。下文列出幾個能夠讓你快速上手的知識點。

安裝

npm install request --save
var request = require('request');

API

  • GET

      request(url,function(error,response,body){
          if(!error && response.statusCode == 200){
              //輸出返回的內容
              console.log(body);
          }
      });
    
  • POST

      var options = { 
        uri: 'https://www.googleapis.com/urlshortener/v1/url', 
        method: 'POST', 
        json: { "longUrl": "http://www.google.com/" }
      };
    
      request({
          url: 'http://xxx.xxx.com',
          method: 'POST',
          body: formData
      }, function(error, response, body) {
          if (!error && response.statusCode == 200) {
              //輸出返回的內容
              console.log(body);
          }
      });
    

任何響應都可以輸出到檔案流。

request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png'))

反過來,也可以將檔案傳給PUT或POST請求。未提供header的情況下,會檢測檔案字尾名,在PUT請求中設定相應的content-type。

fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json'))

表單

request支援 application/x-www-form-urlencodedmultipart/form-data 實現表單上傳。

  • x-www-form-urlencoded:

      request.post('http://service.com/upload', {form:{key:'value'}})
      或者:
      request.post('http://service.com/upload').form({key:'value'})
    
  • multipart/form-data

      var r = request.post('http://service.com/upload')
      var form = r.form()
      form.append('my_field', 'my_value')
      form.append('my_buffer', new Buffer([1, 2, 3]))
      form.append('my_file', fs.createReadStream(path.join(__dirname, 'doodle.png'))
      form.append('remote_file', request('http://google.com/doodle.png'))
    

superagent

superagent它是一個強大並且可讀性很好的輕量級ajax API,是適用於nodejs環境的一個關於HTTP方面的庫。

安裝

npm install superagent --save

簡單使用

一個請求的初始化可以用請求物件裡合適的方法來執行,然後呼叫end()來發送請求。

var superagent = require('superagent');

superagent
    .post('/api')
    .send({
        'key': 'value'
    })
    .set('header_key', 'header_value')
    .end(function(err, res) {
        if (err) {
            //do something
        } else {
            //do something
        }
    })

或
superagetn
    .get(''http://example.com/search'')
    .end(function(res){ });

API

請求方法的引數可以直接使用多個key/value,也可以分多次呼叫請求方法每次傳遞一對key/valu或者key/value字串

  • GET

        //接下來四種方法所形成的URL為/api?name=An&age=20&sex=male
        //第一種
        superagent
            .get(/api)
            .query({name:'liang'})
            .query({age:18})
            .query({sex:'female'})
            .end(function(res){ })
        //第二種
        superagent
            .get(/api)
            .query({name:'liang',age:18,sex:'female'})
            .end(function(res){ })
        //第三種 
        superagent
            .get(/api)
            .query('name=liang&age=18&sex=female')
            .end(function(res){ })
        //第四種
        superagent
            .get(/api)
            .query('name=liang')
            .query('age=18')
            .query('sex=female')
            .end(function(res){ })
    
  • POST

        superagent
            .post('/api')
            .set('Content-Type','application/json')
            .send('{"name":"An","age":20,"sex":"male"}')
            .end(cb)
        //等價於 下面的寫法,因為json是預設的 Content-Type
        superagent
            .post('/api')
            .send({name:"An",age:20,sex:"male"})
            .end(cb)
        //等價於 ==>
        superagent
            .post('/api')
            .send({name:"An"})
            .send({age:20})
            .sex({sex:'male'})
            .end(cb)
    

    superagent的請求資料格式化是可以擴充套件的,不過預設支援form和json兩種格式,想傳送資料以application/x-www-form-urlencoded型別的話,則可以簡單的呼叫.type()方法傳遞form引數就行,這裡預設是json,下面的請求將會發送post name=a&age=18:

      request
        .post('/user') 
        .type('form') 
        .send({ name: 'tj' }) 
        .send({ pet: 'tobi' }) 
        .end(callback)
    

post && get

當用.send(obj)方法來發送一個post請求,並且希望傳遞一些查詢字串,可以呼叫.query()方法,比如向?format=json&dest=/login傳送post請求:

request 
  .post('/') 
  .query({ format: 'json' })
  .query({ dest: '/login' }) 
  .send({ post: 'data', msg: 'hello' }) 
  .end(callback);

請求設定

  • 設定請求頭:呼叫set()方法,引數傳遞一組鍵值對

      superagent
          .get('/api')
          .set({
              'Referer','https://www.google.com',
              'Accept','image/webp,image/*,*/*;q=0.8'
          })
           .end(function(req,res){
              //do something
          })
    
  • Response

響應一般會提供很多有用的標識以及屬性,都在response物件裡,按照respone.text,解析後的response.body,頭欄位,一些標識的順序來排列。

- res.text
    包含未被解析的響應資料

- res.body
    包含解析的資料,跟請求資料自動序列化一樣,響應資料也會自動的解析,
    當為一個Content-Type。定義一個解析器後,就能自動解析,預設解析包
    含application/json和application/x-www-form-urlencoded,可以
    通過訪問res.body來訪問解析物件。

- res.header
    響應頭,res.header包含解析之後的響應頭資料,鍵值都是node處理成小
    寫字母形式,比如res.header['content-length'].

- res.type & res.charset 型別和編碼格式
    Content-Type響應頭欄位是一個特列,伺服器提供res.type來訪問它,
    預設res.charset是空的,如果有的話,則自動填充,例如Content-Type
    值為text/html; charset=utf8,則res.type為text/html,res.charst
    為utf8.

- res.status狀態碼

其他設定

  • req.abort() 終止請求

  • req.timeout(ms) 暫停請求 ms 表示毫秒為單位的時間

  • 管道資料

    nodejs客戶端允許使用一個請求流來輸送資料,比如請求一個檔案作為輸出流:

      var request = require('superagent') ,fs = require('fs');
      var stream = fs.createReadStream('path/to/my.json');
      var req = request.post('/somewhere');
      req.type('json');
      stream.pipe(req);
    

    或者輸送一個響應流到檔案中:

      var request = require('superagent') , fs = require('fs');
      var stream = fs.createWriteStream('path/to/my.json');
      var req = request.get('/some.json');
      req.pipe(stream);
    
  • 錯誤處理

    當傳送錯誤時,superagent首先會檢查回撥函式的引數數量,當err引數提供的話,引數就是兩個,如下:

      request 
        .post('/upload') 
        .attach('image', 'path/to/tobi.png') 
        .end(function(err, res){ });
      ``
      當省略了回撥函式,或者回調只有一個引數的話,可以新增error事件的處理.
      ```js
      request 
        .post('/upload') 
        .attach('image', 'path/to/tobi.png') 
        .on('error', handle) 
        .end(function(res){ });