【问题标题】:Receiving two http responses for one request为一个请求接收两个 http 响应
【发布时间】:2024-01-24 11:16:01
【问题描述】:

我在一个端口上有一个 express 应用,在另一个端口上有一个 react 应用。我的 express 应用程序启用了 cors 并通过 Passport 对用户进行身份验证。这有效,当我通过我的反应应用程序点击登录路线时,我被带到 twitter,提示并重定向到我的反应应用程序。但是,在返回我的反应应用程序后,我从我的快速应用程序中收到一个 404 错误。当我刷新页面时,我收到正确的 200 状态和存储在我的快速应用程序的会话中的用户 JSON 对象。

这就是事情变得奇怪的地方。如果我再次刷新,在我的快速应用程序控制台中,我会收到 200(使用正确的用户),然后是 404 错误(没有用户信息)。在我的反应应用程序控制台上,我看到我只调用了一次向快速应用程序发送请求的函数。

这是我的快递应用代码

var app = express()

app.use(require('morgan')('combined'))
app.use(require('cookie-parser')())
app.use(require('body-parser').urlencoded({ extended: true }))
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }))

app.use(passport.initialize())
app.use(passport.session())

app.use(cors())

app.get('/getUser',
  function (req, res, next) {
    if (req.user) {
      req.session.user = req.user
      return res.status(200).send(req.user)
    } else {
      next()
    }
  }
) 

这就是我在我的 react 应用程序中提出请求的方式。 loginWithTwitter 是从 redux 存储分派的,并在我的 react 组件中的 componentDidMount 内部调用。

export function loginWithTwitter () {
  return function thunk (dispatch) {
    console.log('run once')
    axios({
      method: 'get',
      url: 'http://localhost:8080/getUser',
      responseType: 'json',
      headers: {'Accept': 'application/json'}
    })
      .then(res =>
        res.data
      )
      .then(user => {
        const action = twitterLogin(user)
        dispatch(action)
      })
      .catch(console.error())
  }
}

几个没有意义的怪癖:

  • 如果我使用代理而不是启用 CORS,那么整个事情都可以正常工作(无需刷新且没有 404 错误)。如果我在 safari/chrome 中禁用 CORS 检查,它也可以工作
  • 即使我在浏览器中使用代理或禁用 CORS,我仍然会收到双重 http 响应,但双重调用都返回 200 并提供正确的数据。
  • 这让我相信这是我调用函数的方式的问题,并且可能在其中一个调用中间件没有正确设置 CORS 标头,或者服务器不知道如何处理连续的来电。但是,我显然没有调用 loginWithTwitter 两次,因为我已经在其中登录以检查调用次数。

为什么会这样?

编辑:这是我的快速控制台中双重响应的样子。请注意它们是如何在一秒钟之内完成的。

::1 - - [22/Feb/2018:01:00:18 +0000] "GET /getUser HTTP/1.1" 200 9135 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38"
::1 - - [22/Feb/2018:01:00:19 +0000] "GET /getUser HTTP/1.1" 404 20 "http://localhost:3000/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Safari/604.1.38"

这是我的请求的请求标头

Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:en-US,en;q=0.9
Connection:keep-alive
content-type:application/json; charset=utf-8
Host:localhost:8080
Origin:http://localhost:3000
Referer:http://localhost:3000/
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36

【问题讨论】:

  • 是一个请求 OPTIONS 请求,然后下一个是 GET - 这称为 CORS 预检 - 它不是一个请求的两个响应,而是两个不同请求的两个响应
  • @JaromandaX 它们都是 GET 请求。并且不应该对 GET 请求进行任何预检检查。我也尝试过在 CORS 中间件中禁用 preflight-continue 并遇到类似问题。我将使用我的快速控制台结果更新原始帖子。
  • They are both GET requests - 奇怪,什么浏览器? there shouldn't be any preflight checks for a GET request 你不知道 CORS:p GET/POST 可能需要预检,其他方法,预检是强制性的 - 检查你的请求标头...是否根据 CORS 触发预检文档?
  • 所以......是否涉及CORS?浏览器开发者工具网络标签中显示了什么?
  • @JaromandaX 所以我在使用 safari。但是,我刚刚在 chrome 中进行了测试,并意识到我在 chrome 中只收到一个 404 响应,没有 200。我可以仔细检查是否有任何 CORS 中间件默认标头触发预检。现在我必须弄清楚为什么 safari 会导致多次调用,但是 chrome、postman 甚至直接在 safari 中访问路由而不是使用我的应用程序,都不会导致多次响应。浏览器网络选项卡显示通用 404,而不是访问控制问题,这将是一个 CORS 问题。

标签: javascript node.js reactjs express redux


【解决方案1】:

这是由于设置标题的方式造成的。它正在触发预检,但通过我的 cors 中间件启用预检并没有帮助。如果使用了 OPTIONS 方法(在发送预检请求时触发),我最终做的事情是调用 next() ,这是令人难以置信的 hacky 并且仍然没有解释问题。这会导致调用下一个相关方法,在本例中是我的 getUser 路由。

  if (req.method == 'OPTIONS') { res.send(200) }

  next()

【讨论】:

  • 你试过if (req.method == 'OPTIONS') { res.send(200); res.end(); return; } - 只是一个想法
  • 至少在我的应用程序中,这与调用 next() 类似。现在的问题是,有时 OPTIONS 响应从未发送过,或者在 OPTIONS 响应发送后从未发送过 GET 响应。在我的应用程序中,我正在接收用户,但它很挑剔,有时不起作用,有时会起作用。我可以在我的快速控制台中看到响应失败的地方,因为它只有一个 OPTIONS 响应或只有一个 GET。有没有办法确保它们都被发送?
  • 虽然我有时也会收到两个 GET 响应和一个 OPTIONS 响应。这令人不安。