【问题标题】:Parsing a JSON API in Node在 Node 中解析 JSON API
【发布时间】:2014-02-11 22:56:46
【问题描述】:

我正在为外部 API 构建节点包装器,但在解析 JSON 响应时遇到问题。以下代码发出请求:

https = require "https"
querystring = require "querystring"

API_HOST = "api.lob.com"
API_PATH = "/v1"

startResponseHandler = (req, cb) ->
  if typeof cb isnt "function" then console.log "Error: callback needs to be a function!"
  req.on 'response', (res) ->
    response = ''
    res.setEncoding 'utf8'
    res.on 'data', (stream) ->
      response += stream
    res.on 'end', () ->
      error = null
      try
        response = JSON.parse response
        if res.statusCode != 200 or 201
          response = null
          error = new Error response.error.message
          error.name = response.error.type
          error.code = response.error.code
          error.param = response.error.param
      catch e
        error = new Error "Invalid JSON"
        response = null
      cb error, response
  req.on 'error', (error) ->
    cb error

module.exports = lob = (api_key) ->

  # This function makes the request
  makeRequest = (method, path, data, cb) ->
    data = querystring.stringify data
    options = 
      hostname: API_HOST
      path: "#{API_PATH}/#{path}"
      method: method
      auth: "#{api_key}:"
      headers:
        'Content-Type' : 'application/x-www-form-urlencoded'
        'Content-Length' : data.length
    req = https.request options
    startResponseHandler req, cb
    req.write data if method is "POST" or "PUT"
    req.end()

  # GET, POST, DELETE, PUT functions
  _get = (path, cb) ->
    makeRequest "GET", path, {}, cb

  _post = (path, data, cb) ->
    makeRequest "POST", path, data, cb

  _del = (path, cb) ->
    makeRequest "DELETE", path, {}, cb

  _put = (path, data, cb) ->
    makeRequest "PUT", path, data, cb

  # Jobs
  jobs:

    createJob: (data, cb) ->
      _post "/jobs/", data, cb

下面的代码是用来测试包装器的:

api_key = "test_0dc8d51e0acffcb1880e0f19c79b2f5b0cc"

lob     = require('../src/lob')(api_key)
should  = require("should")
chai    = require("chai")

data = 
  name: "Michigan fan letter"
  to: "adr_43769b47aed248c2"
  from: "adr_7f9ece71fbca3796"
  object1: "obj_7ca5f80b42b6dfca"
  object2: "obj_12128d3aad2aa98f"

describe "Job", ->
  @timeout(10000)
  describe "create", ->
    it "should create a job with address_id", (done) ->
      lob.jobs.createJob data , (new_job) ->
        new_job['name'].should.equal(data['name'])
        done()

但是,当我运行 mocha 测试 ($ mocha --compilers coffee:coffee-script) 时,我收到以下错误:

1) Job create should create a job with address_id:

      + expected - actual

      +"Michigan fan letter"
      -"Error"

编辑

以下是来自stream 内部res.on 'data' 的回复:

{
    "id": "job_7ecc50bea15178b8e07a",
    "name": "Michigan fan letter",
    "price": "1.26",
    "to": {
        "id": "adr_43769b47aed248c2",
        "name": "Harry Zhang",
        "email": "harry@lob.com",
        "phone": "5555555555",
        "address_line1": "123 Test Street",
        "address_line2": "Unit 199",
        "address_city": "Mountain View",
        "address_state": "CA",
        "address_zip": "94085",
        "address_country": "UNITED STATES",
        "date_created": "2013-07-20T05:53:25+00:00",
        "date_modified": "2013-07-20T05:53:25+00:00",
        "object": "address"
    },
    "from": {
        "id": "adr_7f9ece71fbca3796",
        "name": "Harry Zhang",
        "email": "harry@lob.com",
        "phone": "5555555555",
        "address_line1": "123 Test Avenue",
        "address_line2": "Unit 401",
        "address_city": "Seattle",
        "address_state": "WA",
        "address_zip": "98122",
        "address_country": "UNITED STATES",
        "date_created": "2013-07-20T05:55:19+00:00",
        "date_modified": "2013-07-20T05:55:19+00:00",
        "object": "address"
    },
    "status": "processed",
    "tracking": null,
    "packaging": {
        "id": "1",
        "name": "Smart Packaging",
        "description": "Automatically determined optimal packaging for safe and secure delivery",
        "object": "packaging"
    },
    "service": null,
    "objects": [
        {
            "id": "obj_7ca5f80b42b6dfca",
            "name": "Michigan is great",
            "quantity": "1",
            "full_bleed": "0",
            "double_sided": "0",
            "date_created": "2013-07-20T05:57:32+00:00",
            "date_modified": "2013-07-20T05:57:32+00:00",
            "setting": {
                "id": "101",
                "type": "Documents",
                "description": "Color Document",
                "paper": "20lb Paper Standard",
                "width": "8.500",
                "length": "11.000",
                "color": "Color",
                "notes": "50 cents per extra page",
                "object": "setting"
            },
            "url": "http://assets.lob.com/obj_7ca5f80b42b6dfca",
            "object": "object"
        },
        {
            "id": "obj_12128d3aad2aa98f",
            "name": "GO BLUE",
            "quantity": "1",
            "full_bleed": "0",
            "double_sided": "0",
            "date_created": "2013-07-31T00:58:35+00:00",
            "date_modified": "2013-07-31T00:58:35+00:00",
            "setting": {
                "id": "100",
                "type": "Documents",
                "description": "Black and White Document",
                "paper": "20lb Paper Standard",
                "width": "8.500",
                "length": "11.000",
                "color": "Black and White",
                "notes": "12 cents per extra page",
                "object": "setting"
            },
            "url": "http://assets.lob.com/obj_12128d3aad2aa98f",
            "object": "object"
        }
    ],
    "date_created": "2014-01-25T03:10:10+00:00",
    "date_modified": "2014-01-25T03:10:10+00:00",
    "object": "job"
}

documentation 中的示例响应完全匹配

{
    "id": "job_754d8b14dd31587d6873",
    "name": "Michigan fan letter",
    "price": "0.96",
    "to": {
        "id": "adr_43769b47aed248c2",
        "name": "Harry Zhang",
        "email": "harry@lob.com",
        "phone": "5555555555",
        "address_line1": "123 Test Street",
        "address_line2": "Unit 199",
        "address_city": "Mountain View",
        "address_state": "CA",
        "address_zip": "94085",
        "address_country": "UNITED STATES",
        "date_created": "2013-07-20T05:53:25+00:00",
        "date_modified": "2013-07-20T05:53:25+00:00",
        "object": "address"
    },
    "from": {
        "id": "adr_7f9ece71fbca3796",
        "name": "Harry Zhang",
        "email": "harry@lob.com",
        "phone": "5555555555",
        "address_line1": "123 Test Avenue",
        "address_line2": "Unit 401",
        "address_city": "Seattle",
        "address_state": "WA",
        "address_zip": "98122",
        "address_country": "UNITED STATES",
        "date_created": "2013-07-20T05:55:19+00:00",
        "date_modified": "2013-07-20T05:55:19+00:00",
        "object": "address"
    },
    "status": "processed",
    "tracking": null,
    "packaging": {
        "id": "1",
        "name": "Smart Packaging",
        "description": "Automatically determined optimal packaging for safe and secure delivery",
        "object": "packaging"
    },
    "service": null,
    "objects": [
        {
            "id": "obj_7ca5f80b42b6dfca",
            "name": "Michigan is great",
            "quantity": "1",
            "full_bleed": "0",
            "double_sided": "0",
            "date_created": "2013-07-20T05:57:32+00:00",
            "date_modified": "2013-07-20T05:57:32+00:00",
            "setting": {
                "id": "101",
                "type": "Documents",
                "description": "Color Document",
                "paper": "20lb Paper Standard",
                "width": "8.500",
                "length": "11.000",
                "color": "Color",
                "notes": "50 cents per extra page",
                "object": "setting"
            },
            "object": "object"
        }
    ],
    "date_created": "2014-01-18T19:52:27+00:00",
    "date_modified": "2014-01-18T19:52:27+00:00",
    "object": "job"
}

这是来自JSON.parse response的数据

{ id: 'job_9973e060bd8147f97f5f',
  name: 'Michigan fan letter',
  price: '1.26',
  to:
   { id: 'adr_43769b47aed248c2',
     name: 'Harry Zhang',
     email: 'harry@lob.com',
     phone: '5555555555',
     address_line1: '123 Test Street',
     address_line2: 'Unit 199',
     address_city: 'Mountain View',
     address_state: 'CA',
     address_zip: '94085',
     address_country: 'UNITED STATES',
     date_created: '2013-07-20T05:53:25+00:00',
     date_modified: '2013-07-20T05:53:25+00:00',
     object: 'address' },
  from:
   { id: 'adr_7f9ece71fbca3796',
     name: 'Harry Zhang',
     email: 'harry@lob.com',
     phone: '5555555555',
     address_line1: '123 Test Avenue',
     address_line2: 'Unit 401',
     address_city: 'Seattle',
     address_state: 'WA',
     address_zip: '98122',
     address_country: 'UNITED STATES',
     date_created: '2013-07-20T05:55:19+00:00',
     date_modified: '2013-07-20T05:55:19+00:00',
     object: 'address' },
  status: 'processed',
  tracking: null,
  packaging:
   { id: '1',
     name: 'Smart Packaging',
     description: 'Automatically determined optimal packaging for safe and secure delivery',
     object: 'packaging' },
  service: null,
  objects:
   [ { id: 'obj_7ca5f80b42b6dfca',
       name: 'Michigan is great',
       quantity: '1',
       full_bleed: '0',
       double_sided: '0',
       date_created: '2013-07-20T05:57:32+00:00',
       date_modified: '2013-07-20T05:57:32+00:00',
       setting: [Object],
       url: 'http://assets.lob.com/obj_7ca5f80b42b6dfca',
       object: 'object' },
     { id: 'obj_12128d3aad2aa98f',
       name: 'GO BLUE',
       quantity: '1',
       full_bleed: '0',
       double_sided: '0',
       date_created: '2013-07-31T00:58:35+00:00',
       date_modified: '2013-07-31T00:58:35+00:00',
       setting: [Object],
       url: 'http://assets.lob.com/obj_12128d3aad2aa98f',
       object: 'object' } ],
  date_created: '2014-01-25T23:12:37+00:00',
  date_modified: '2014-01-25T23:12:37+00:00',
  object: 'job' }

【问题讨论】:

  • 你看过stream 里面的res.on 'data' 看看它是否是你所期望的吗? response 内部的 res.on 'end' 相同。
  • 我没有记录内容,但返回了一个 201 标头,表明 POST 请求已成功发出
  • 开始记录它,如果JSON.parse 抱怨,那么你可能没有得到你认为你得到的数据。
  • 好建议!我已经用JSON.parse 的回复更新了这个问题
  • 那是 JSON 字符串还是 JavaScript 对象? JSON.parse 需要一个字符串。

标签: javascript json node.js coffeescript mocha.js


【解决方案1】:

我在该代码中发现了三个问题。我通常不使用 CoffeeScript。所以如果我看错了,请纠正我:

  1. 回调是这样调用的:

    cb error, response
    

    第一个参数是错误对象,第二个参数是响应,但是在测试中回调是这样的:

    lob.jobs.createJob data , (new_job) ->
             new_job['name'].should.equal(data['name'])
    

    所以new_job 是错误对象。然而,这并不能解释一切,因为您正在遇到错误。但是即使没有错误,new_job 的值也不会是响应。

    测试显示字符串Error 是实际值的原因是Error 对象有一个name 字段,该字段设置为异常类的名称。 (所以new Error().name 的计算结果为"Error"。)

  2. 这部分代码看起来也不正确:

    try
      response = JSON.parse response
      if res.statusCode != 200 or 201
        response = null
        error = new Error response.error.message
        error.name = response.error.type
        error.code = response.error.code
        error.param = response.error.param
    catch e
      error = new Error "Invalid JSON"
      response = null
    cb error, response
    

    if 分支中,response 设置为null,然后访问response 的一些字段。这将导致异常。由于 try... catch 子句的设置方式,此异常将被解释为错误的 JSON。 try... catch 子句应缩小到仅涵盖 JSON.parse 调用:

    try
      response = JSON.parse response
    catch e
      error = new Error "Invalid JSON"
      response = null
    
  3. 这个测试不正确:

      if res.statusCode != 200 or 201
    

    变成JavaScript:

      if (res.statusCode !== 200 || 201)
    

    || 之后的部分使其始终为真。类似于以下 CoffeeScript 代码的内容似乎是预期的:

      if res.statusCode not in [200, 201]
    

所以请求是否成功并不重要。因为第三个问题,if 测试总是为真,分支总是会被取走,第二个问题总是会出现。所以 Mocha 测试总是会失败。

【讨论】:

  • 快速说明 - 使用此设置,如果我加入 console.log res.statusCode,它会返回 200
  • 所以它不能解决你眼前的问题。很公平,但它仍然是糟糕的代码。
  • 加上我添加的第三点,我可能已经涵盖了所有内容。
猜你喜欢
  • 2015-01-10
  • 1970-01-01
  • 1970-01-01
  • 2015-07-25
  • 1970-01-01
  • 1970-01-01
  • 2019-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多