网络埋伏纪事

Node Hero - 6. Node.js Request 模块

网络埋伏纪事 · 2016-11-21翻译 · 396阅读 原文链接

在如下教程中,将学习 HTTP 协议的基础知识,以及如何使用 Node.js 的 request 模块从外部源获取资源。

什么是 HTTP?

HTTP 代表超文本传输协议(Hypertext Transfer Protocol)。HTTP 作为在客户机-服务器计算模型中的请求-响应协议。

HTTP 状态码

在深入与其它 API 的通讯之前,我们来回顾一下在此过程中会遇到的 HTTP 状态码。状态码描述了请求的结果,它对错误处理是必不可少的。

  • 1xx - 响应还没完成(Informational)

  • 2xx - 成功:这些状态码表示请求被接收并正确处理了。最常见的成功响应码是 200 OK201 Created204 No Content

  • 3xx - 重定向: 这组状态码表示要完成请求,客户端必须要做一些额外的操作。最常见的重定向响应码是 301 Moved Permanently304 Not Modified

  • 4xx - 客户端错误: 这类状态码被用在客户端发送的请求出现了某种错误时。服务器响应通常会包含了对错误的解释。最常见的客户端错误码是 400 Bad Request401 Unauthorized403 Forbidden404 Not Found409 Conflict

  • 5xx - 服务器错误: 当服务器由于某些错误不能完成有效的请求时,就发送这些状态码。原因可能是代码中的一个 bug,或者一些临时或者永久性的无能。最常见的服务器错误状态码是 500 Internal Server Error503 Service Unavailable。如果你想学习更多有关 HTTP 状态码的知识,可以在这里找到详细的解释。

向外部 API 发送请求

在 Node 中,连接到外部 API 是很简单的。只需要 require 核心 HTTP 模块,然后开始发送请求即可。

当然,有更好的方式来调用外部端点。在 NPM 上可以找到很多让这个过程更容易的模块。例如,两个最流行的模块是 requestsuperagent

这两个模块都有错误优先的回调接口,这就会导致一些问题(我打赌你已经听说过回调地狱),但是幸运的是我们可以访问封装了 Promise 的版本。

使用 Node.js Request 模块

request-promise 模块 的使用很简单。从 NPM 安装它之后,只需要 require 它:

const request = require('request-promise')

发送一个 GET 请求也那么简单:

const options = {  
  method: 'GET',
  uri: 'https://risingstack.com'
}
​
request(options)  
  .then(function (response) {
    // Request was successful, use the response object at will
  })
  .catch(function (err) {
    // Something bad happened, handle the error
  })

如果调用一个 JSON API,你可能会想让 request-promise 自动解析响应。此时,只需要将其添加到 request 选项即可:

json: true

POST 请求的工作方式也类似:

const options = {  
  method: 'POST',
  uri: 'https://risingstack.com/login',
  body: {
    foo: 'bar'
  },
  json: true 
    // JSON stringifies the body automatically
}
​
request(options)  
  .then(function (response) {
    // Handle the response
  })
  .catch(function (err) {
    // Deal with the error
  })

要添加查询字符串参数,只需要将 qs 属性添加到 options 对象:

const options = {  
  method: 'GET',
  uri: 'https://risingstack.com',
  qs: {
    limit: 10,
    skip: 20,
    sort: 'asc'
  }
}

这会让请求 URL 变为:https://risingstack.com?limit=10&skip=20&sort=asc

还可以用添加查询参数同样的方式来定义所有请求头:

const options = {  
  method: 'GET',
  uri: 'https://risingstack.com',
  headers: {
    'User-Agent': 'Request-Promise',
    'Authorization': 'Basic QWxhZGRpbjpPcGVuU2VzYW1l'
  }
}

错误处理

错误处理是对外部 API 发起请求的重要部分,因为不能确保会发生什么。除了客户端错误以外,服务器会响应一个错误,或者就发送一个错误或者不一致格式的数据。在试着处理响应时,要记住这些。此外,对每次请求都使用 catch 也是一个避免外部服务让服务器崩溃的好办法。

综合

既然已经学过如何创建一个 Node.js HTTP 服务器,如何渲染 HTTP 页面,如何从外部 API 获取数据,那么现在就到了将它们放在一起的时候了!

在本例中,我们将创建一个小型 Express 应用程序,该程序基于城市名渲染当前的天气条件。

要获取 AccuWeather​ API key,请访问 Accuweather 开发者网站

const express = require('express')  
const rp = require('request-promise')  
const exphbs = require('express-handlebars')

const app = express()

app.engine('.hbs', exphbs({  
  defaultLayout: 'main',
  extname: '.hbs',
  layoutsDir: path.join(__dirname, 'views/layouts')
}))
app.set('view engine', '.hbs')  
app.set('views', path.join(__dirname, 'views'))

app.get('/:city', (req, res) => {  
  rp({
    uri: 'http://apidev.accuweather.com/locations/v1/search',
    qs: {
      q: req.params.city,
      apiKey: 'api-key'
         // Use your accuweather API key here
    },
    json: true
  })
    .then((data) => {
      res.render('index', data)
    })
    .catch((err) => {
      console.log(err)
      res.render('error')
    })
})

app.listen(3000)

上面的示例做如下事情:

  • 创建一个 Express 服务器
  • 设置 handlebars 结构 - 对于 .hbs 文件,请参考 Node.js HTTP 教程
  • 发送请求给外部 API
    • 如果一切顺利,就渲染页面
    • 否则,就显示错误页并记录错误

下一步

下章将学习如何正确组织 Node.js 项目