node.js express,一个很奇怪的行为

Posted

技术标签:

【中文标题】node.js express,一个很奇怪的行为【英文标题】:node.js express, a very strange behaviour 【发布时间】:2017-12-12 10:11:20 【问题描述】:

我有一个 Express 服务器正在运行。

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

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

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

但是,由于 Google 每秒不接受超过 50 次对其地理编码 API 的请求,所以我必须使用 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 来做前端工作并使用 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)
  )

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

我也会在这里提供我的代码来记录每个请求的信息

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 并选择文件并上传,它工作正常 - 日志中只有一个请求。

谁能告诉我是什么原因。我觉得这是 Express 的错误。我的 Express 版本是 4.15.2

【问题讨论】:

您可能想使用***.com/posts/44982171/edit 来编辑/更新您的问题,并在浏览器开发工具中添加网络窗格显示的日志。如果这表明浏览器实际上实际上发送了两次 POST 请求,那么这不会是 Express 中的错误。另一方面,如果这没有显示浏览器发送了两次,而是 Express 以某种方式处理了浏览器实际上没有发送的第二个 POST 请求,那么是的,这显然是 Express 中的一个错误 您是否有可能看到两个 POST 请求,因为第一个来自您的 startUploadFile() 函数,第二个是默认表单帖子(由您从和调用 startUploadFile() 的同一个按钮触发由浏览器自动执行)?如果是这样,您需要阻止按钮的默认操作。如果这可能是错误,我们可以通过查看上传的 HTML 以及如何调用 startUploadFile() 来更具体地提供帮助。 @jfriend00 谢谢,但我不使用表单中的按钮,我只是在其上声明一个按钮和一个点击处理程序。 startUploadFile() 是点击处理程序。而且因为我的 api 服务器与这个 HTML 页面有不同的域名,如果表单自动提交它也无法到达我的 api 服务器。 你确定startUploadFile()被调用一次还是两次? @sideshowbarker 在我的 chrome 网络监控工具中,有 2 个请求,但第一个是 OPTIONS 请求,第二个是真正的 POST 请求。没有任何其他 POST 请求。 【参考方案1】:

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

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

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

【讨论】:

我知道 OPTIONS 请求,但我不明白的是第二个 POST 请求。请仔细看我附上的截图,你会看到第二个 POST 请求。我不明白它为什么会出现,因为我只调用了一次 startUploadFile()。 第二个 POST 请求与第一个重叠。所以在我的例子中,有两个进程在做这项工作。【参考方案2】:

好吧,最后我可以通过将“Access-Token”传递到请求正文而不是请求标头来解决这个问题(现在,我的服务器总是只接收一个请求)。感谢@sideshowbarker,因为您的评论促使我采用这种方法。

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

【讨论】:

以上是关于node.js express,一个很奇怪的行为的主要内容,如果未能解决你的问题,请参考以下文章

node.js 中的奇怪行为

SQL - node.js express - 反应加载行为

Image Magick Module 抛出奇怪的错误(Express js Node js)

Node.JS MySQL 将结果记录到控制台而不被要求和奇怪的行为

Express / Node.js页面刷新问题

Node.js Express Spotify API保存在会话中