node.js表达,一种非常奇怪的行为

时间:2017-07-08 03:23:36

标签: javascript node.js express cors fetch-api

我正在运行Express服务器。

我所做的是从HTML上传Excel文件,然后Express解析该文件并进行一些计算。

在Excel文件中,每一行都包含有关用户地址的信息。

对于每个地址,我们的Express服务器将使用谷歌地图地理编码API来计算纬度和数据。经度。

但是,由于Google每秒对其地理编码API不接受超过50个请求,因此我必须使用settimeout来延迟计算。

例如,如果Excel文件有50个地址,那么我必须在每个地址上使用settimeout以避免速率限制。

这是我使用settimeout并计算纬度,经度

的代码

createFromFile(req, res) { 
    var form = new formidable.IncomingForm();
    return form.parse(req, function (err, fields, files) {
      if (err)
        return res.status(500).json({error: err.message})

      var workbook = new exceljs.Workbook();
      return workbook.xlsx.readFile(files.excelfile.path).then(function() {
        // use workbook
        var worksheet = workbook.getWorksheet(1)
        var data = []
        for (var i=2; i<=worksheet.rowCount; i++) {
          data.push({
            from_name: worksheet.getCell('A'+i).value + '',
            from_address: worksheet.getCell('B'+i).value + '',
            from_phone: worksheet.getCell('C'+i).value + '',
            receiver_name: worksheet.getCell('D'+i).value + '',
            receiver_address: worksheet.getCell('E'+i).value + '',
            receiver_phone: worksheet.getCell('F'+i).value + '',
            note: worksheet.getCell('H'+i).value + ''
          })
        }

        var delay = function(t) {
          return new Promise(function(resolve)           {
            setTimeout(resolve, t)
          })
        }

        return Promise.all(data.map(function(item, i) {
          return function() {
            return delay(750*i).then(function() {
              winston.log('debug', 'process for item '+i)
              return geocoder.geocode(item.from_address).then(function(geo_data) {
                data[i].from_coord = {
                  lat: geo_data[0].latitude,
                  lng: geo_data[0].longitude
                }
                return geocoder.geocode(item.receiver_address).then(function(geo_data) {
                  data[i].receiver_coord = {
                    lat: geo_data[0].latitude,
                    lng: geo_data[0].longitude
                  }
                })
              })
              .catch(function(geo_error) {
                winston.log('error', 'geo_error', {error: geo_error})
                throw new Error('Address in line ' + i + ' is not valid')
              })
            })
          }()
        }))
        .then(function() {
          winston.log('debug', 'we are done calculating location')
          return res.status(201).json(data)
        })
      })
      .catch(function(e) {
        winston.log('error', 'an error occurred')
        return res.status(500).json({error: e.message})
      })
    })
  }

下面是我调用Express API的代码,我使用React做前端工作&amp;使用javascript fetch api向服务器请求。

startUploadFile() {
  this.props.showLoader()
  let data = new FormData()
  data.append('excelfile', this.state.selectedFile)
  data.append('name', 'excelfile')
  var me = this
  fetch(Const.restServer + '/create-from-file', {
    headers: {
      'Access-Token': this.props.access_token,
    },
    method: 'POST',
    body: data
  })
  .then(function(r) {
    return r.json()
  })
  .then(function(r) {
    if (r.hasOwnProperty('error'))
      throw new Error(r.error)

    me.props.hideLoader()
    me.props.showDialog('success', 'Process Complete')
  })
  .catch(function(e) {
    console.log(e)
    me.props.hideLoader()
    me.props.showDialog('error', e.message)
  })
}

我的问题是当我使用上面的代码在浏览器上上传文件时,我在快速日志文件中看到两个请求。像这样:

Express Server Log File 我也会提供我的代码来记录每个请求的信息

app.use(function(req, res, next) {
  winston.log('debug', 'call api:', {
    api: req.url, requestMethod: req.method
  })
  res.header("Access-Control-Allow-Origin", "*");
  res.header(
    "Access-Control-Allow-Headers",
    "Origin, X-Requested-With, Content-Type, Accept, Access-Token, Web-Admin-Request"
  );
  next()
});

function isAuthenticated(req, res, next) {
  /**
  ** Rest API Middleware check if access_token is valid
  */
  let accessToken = req.body.accessToken || req.get('Access-Token')

  // bunch of codes to check accessToken
  next()
}

app.post('/order/create-from-file', isAuthenticated, orderController.createFromFile);

我不明白为什么会这样。如果我使用Postman&amp;选择一个文件&amp;上传,它工作正常 - 日志中只有一个请求。

谁能告诉我是什么原因。我觉得这是一个快递的Bug。我的快递版本是4.15.2

2 个答案:

答案 0 :(得分:2)

您的代码添加到请求的Access-Token请求标头会在尝试OPTIONS请求之前触发浏览器发送CORS预检POST请求。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Preflighted_requests提供了更多详细信息,但其中的要点是,只要您的代码将Access-Token标头添加到请求中,就无法阻止浏览器进行额外的预检{{1请求 - 因为它是浏览器自动作为CORS协议的一部分。

当您使用Postman时没有看到额外请求的原因是它不执行CORS预检OPTIONS仅请求的浏览器发送它,并且仅针对XHR / Fetch从前端发出的请求在特定来源的浏览器中运行的JavaScript代码(Postman不是)。

答案 1 :(得分:1)

好吧,最后我可以通过将'Access-Token'传递给请求主体而不是请求头来解决这个问题(现在,我的服务器总是只收到一个请求)。感谢@sideshowbarker,因为您的评论会触发我执行此方法。

我仍然认为这个问题是Express中的一个错误,但因为它没有在我的本地开发环境中发生,所以我不会向他们发送报告。