在Node中解析JSON API

时间:2021-07-06 14:54:13

I'm building a node wrapper for an external API and am having issues parsing the JSON response. The following code makes the request:

我正在为外部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

And the below code was written to test the wrapper:

编写下面的代码来测试包装器:

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()

However, when I run the mocha test ($ mocha --compilers coffee:coffee-script) I receive the following error:

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

1) Job create should create a job with address_id:

      + expected - actual

      +"Michigan fan letter"
      -"Error"

EDIT

Below is the response from stream inside res.on 'data':

以下是来自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"
}

Which exactly matches up to the example response in the 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"
}

Here is the data from JSON.parse response

这是来自JSON.parse响应的数据

{ 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' }

1 个解决方案

#1


2  

I see three problems in that code. I don't usually use CoffeeScript. So if I'm misreading please correct me:

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

  1. The callback is called like this:

    回调被调用如下:

    cb error, response
    

    First argument is an error object and the second argument is the response, however in the test the callback is like this:

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

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

    So new_job is the error object. This does not explain everything, however, because you are getting an error. But even if there were no error, the value of new_job would not be the response.

    所以new_job是错误对象。但是,这并不能解释所有内容,因为您收到了错误。但即使没有错误,new_job的值也不会是响应。

    The reason the test shows that the string Error is the actual value is that Error objects have a name field which is set to the name of the class of the exception. (So new Error().name evaluates to "Error".)

    测试显示字符串Error是实际值的原因是Error对象具有名称字段,该字段设置为异常类的名称。 (所以新的Error()。name评估为“Error”。)

  2. This section of code also looks incorrect:

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

    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
    

    In the if branch the response is set to null and then some fields of response are accessed. This will cause an exception. And this exception will then be interpreted as bad JSON due to the way the try... catch clause is set. The try... catch clause should be narrowed to cover only the JSON.parse call:

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

    try
      response = JSON.parse response
    catch e
      error = new Error "Invalid JSON"
      response = null
    
  3. This test is incorrect:

    此测试不正确:

      if res.statusCode != 200 or 201
    

    It becomes JavaScript:

    它变成了JavaScript:

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

    The part after the || makes it always true. Something like the following CoffeeScript code seems to be what is intended:

    ||之后的部分使它永远是真的。像下面的CoffeeScript代码似乎是预期的:

      if res.statusCode not in [200, 201]
    

So it does not matter if the request is successful or not. Because of the third problem, theif test will always be true, the branch always will be taken and the second problem will always occur. So the Mocha test will always fail.

因此,请求是否成功无关紧要。由于第三个问题,theif测试将始终为真,分支始终将被采用,并且第二个问题将始终发生。所以摩卡测试总会失败。

#1


2  

I see three problems in that code. I don't usually use CoffeeScript. So if I'm misreading please correct me:

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

  1. The callback is called like this:

    回调被调用如下:

    cb error, response
    

    First argument is an error object and the second argument is the response, however in the test the callback is like this:

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

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

    So new_job is the error object. This does not explain everything, however, because you are getting an error. But even if there were no error, the value of new_job would not be the response.

    所以new_job是错误对象。但是,这并不能解释所有内容,因为您收到了错误。但即使没有错误,new_job的值也不会是响应。

    The reason the test shows that the string Error is the actual value is that Error objects have a name field which is set to the name of the class of the exception. (So new Error().name evaluates to "Error".)

    测试显示字符串Error是实际值的原因是Error对象具有名称字段,该字段设置为异常类的名称。 (所以新的Error()。name评估为“Error”。)

  2. This section of code also looks incorrect:

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

    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
    

    In the if branch the response is set to null and then some fields of response are accessed. This will cause an exception. And this exception will then be interpreted as bad JSON due to the way the try... catch clause is set. The try... catch clause should be narrowed to cover only the JSON.parse call:

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

    try
      response = JSON.parse response
    catch e
      error = new Error "Invalid JSON"
      response = null
    
  3. This test is incorrect:

    此测试不正确:

      if res.statusCode != 200 or 201
    

    It becomes JavaScript:

    它变成了JavaScript:

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

    The part after the || makes it always true. Something like the following CoffeeScript code seems to be what is intended:

    ||之后的部分使它永远是真的。像下面的CoffeeScript代码似乎是预期的:

      if res.statusCode not in [200, 201]
    

So it does not matter if the request is successful or not. Because of the third problem, theif test will always be true, the branch always will be taken and the second problem will always occur. So the Mocha test will always fail.

因此,请求是否成功无关紧要。由于第三个问题,theif测试将始终为真,分支始终将被采用,并且第二个问题将始终发生。所以摩卡测试总会失败。