本地重定向出现了一次神秘的GET请求,原因不明。

huangapple go评论73阅读模式
英文:

Local redirect sends a ghost GET request for some reason

问题

Here is the translated code part:

我正在尝试创建一个具有以下行为的网站
- 如果未经身份验证的用户尝试访问/home页面他将被重定向到/login页面
- 一旦他通过/login页面登录他将收到一个会话Cookie并被重定向到/home页面
- 如果用户返回到/login页面但具有有效的会话Cookie他将自动重定向到/home页面

我正在使用Node.js和Express
以下是我目前使用的代码
服务器端
```js
app.get('/login', (req, res) => {   
    res.sendFile(__dirname + "/html/login.html")
});

app.post('/login', (req, res) => {
    //尝试验证用户
    //如果用户ID或密码无效
    res.status(401).end();
    //否则创建Cookie并注册会话
    res.cookie("session_token", sessionToken, { sameSite: 'Strict', expires: expiresAt })
    res.cookie("session_id", id, { sameSite: 'Strict', expires: expiresAt })
    res.status(200).send()
});

app.get('/home', (req, res) => {
    //如果无法验证用户
    if (!function_to_authenticate_the_user(req)) return res.redirect(308, '/login')
    //否则
    res.status(200).sendFile(__dirname + "/html/home.html")
});

app.get('/isSessionValid', loginRL, (req, res) => {
    if (function_to_authenticate_the_user(req)) return res.status(200).send();
    else return res.status(401).send()  
});

用户端(函数由按钮的onclick属性触发):

function login(id, mdp) {
    fetch('/login', {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            'id': id,
            'password': password
        })
    }).then(res => {
        if (res.status === 401) {
            //显示错误消息
        } else if (res.status === 200) {
            location.replace('/home')
        }
    })
}

window.addEventListener('load', () => {
    if (!document.cookie) return
    const cookies = Object.fromEntries(document.cookie.split('; ').map(c => c.split('=')));
    if (!cookies.session_token || !cookies.session_id) return
    fetch('/isSessionValid', {
        method: 'GET',
    }).then(res => {
        if (res.status === 200) location.replace('/home')
    })
});

问题:

由于某种我不理解的原因,一旦用户发送POST请求到/login并收到带有Cookie的200响应,它会发送GET请求到/home页面(这是应该的),但还会发送另一个GET请求到/login页面(我不明白为什么)。

它同时发送这两个请求。并且由于(第二个)/login请求被优先处理,浏览器会自动将GET /home解释为308状态(服务器从未收到任何关于/home的请求)。

由于用户现在具有有效的会话Cookie,浏览器尝试再次重定向到/home,但还会自动发送另一个GET请求到/login,一直循环。

我尝试了服务器端和用户端的调试。出于某种原因,服务器似乎从未收到任何来自/home的GET请求。

我尝试删除窗口加载事件监听器,但仍然会发送另一个GET请求到/login,并且无法成功重定向到/home(只是停止了请求的无限循环)。

当我手动将URL设置为从当前/login页面到/home时,会发生相同的行为(一个幽灵请求到/login,不加载/home)。

我尝试更改服务器代码如下:

app.get('/home', (req, res) => {
    console.log('received a request')
    if (!function_to_authenticate_the_user(req)) {
        console.log('trying to redirect')
        return res.redirect(301, '/login') 
    }
    res.status(200).sendFile(__dirname + "/html/home.html")
});

但浏览器仍然在控制台中显示以下请求:

GET http://localhost:8000/home [HTTP/1.1 308 Permanent Redirect 0ms]
GET http://localhost:8000/login [HTTP/1.1 200 OK 0ms]

服务器不会记录任何内容。

我尝试删除用户端代码中的location.replace('/home'),但没有解决问题。

我漏掉了什么?


<details>
<summary>英文:</summary>

I&#39;m trying to create a site with the following behavior :
- If the user attempts to get to the /home page without being authenticated, he gets redirected to the /login page.
- Once he logs in through the /login page, he receive a session cookie and gets redirected to the /home page
- If the user gets back to the /login page but has a valid session cookie, he gets automatically redirected to the /home page

I&#39;m using node.js with express.
Here is the current code I use :
Server-side :
```js
app.get(&#39;/login&#39;, (req, res) =&gt; {   
    res.sendFile(__dirname + &quot;/html/login.html&quot;)
});

app.post(&#39;/login&#39;, (req, res) =&gt; {
    //try to authenticate the user
    //if invalid user_id or password
    res.status(401).end();
    //else creating a cookie and registering the session
    res.cookie(&quot;session_token&quot;, sessionToken, { sameSite: &#39;Strict&#39;, expires: expiresAt })
    res.cookie(&quot;session_id&quot;, id, { sameSite: &#39;Strict&#39;, expires: expiresAt })
    res.status(200).send()
});

app.get(&#39;/home&#39;, (req, res) =&gt; {
    //if the user can&#39;t be authenticated 
    if (!function_to_authenticate_the_user(req)) return res.redirect(308,&#39;/login&#39;)
    //else
    res.status(200).sendFile(__dirname + &quot;/html/home.html&quot;)
});

app.get(&#39;/isSessionValid&#39;, loginRL, (req, res) =&gt; {
    if (function_to_authenticate_the_user(req)) return res.status(200).send();
    else return res.status(401).send()  
});

User-side (function is triggered by an onclick property on a button) :

function login(id, mdp) {
    fetch(&#39;/login&#39;, {
        method: &#39;POST&#39;,
        headers: {
            &#39;Accept&#39;: &#39;application/json&#39;,
            &#39;Content-Type&#39;: &#39;application/json&#39;,
        },
        body: JSON.stringify({
            &#39;id&#39;: id,
            &#39;password&#39;: password
        })
    }).then(res =&gt; {
        if (res.status === 401) {
            // display error message
        } else if (res.status === 200) {
            location.replace(&#39;/home&#39;)
        }
    })
}

window.addEventListener(&#39;load&#39;, () =&gt; {
    if (!document.cookie) return
    const cookies = Object.fromEntries(document.cookie.split(&#39;; &#39;).map(c =&gt; c.split(&#39;=&#39;)));
    if (!cookies.session_token || !cookies.session_id) return
    fetch(&#39;/isSessionValid&#39;, {
        method: &#39;GET&#39;,
    }).then(res =&gt; {
        if (res.status === 200) location.replace(&#39;/home&#39;)
    })
});

The issue :

For some reason I don't understand, once the user sends a POST request to /login and get a 200 response with a cookie, it sends a GET request to the /home page (which is what it should do) BUT ALSO another GET request to the /login page (and I don't understand why)

It sends both requests at the same time. And for some reason the (second) /login request gets prioritized and the browser automatically interprets the GET /home as a 308 status (the server NEVER receive any request to /home)

And since the user now has a valid session cookie, the browser tries again to redirect to /home but also automatically sends another GET request to /login and over and over again.

I tried to debug both server-side and user-side.
For some reason, the server seems to never get any GET request from /home
I tried to remove the window load event listener but it stills sends another get request to /login and never redirects successfully to /home (it just stops the never ending loop of requests)

When I manually sets the url to /home from a current /login page, the same behavior happens (a ghost request to /login and doesn't load /home)
I tried to change the server code to :

app.get(&#39;/home&#39;, (req, res) =&gt; {
    console.log(&#39;received a request&#39;)
    if (!function_to_authenticate_the_user(req)) {
        console.log(&#39;trying to redirect&#39;)
        return res.redirect(301,&#39;/login&#39;) 
    }
    res.status(200).sendFile(__dirname + &quot;/html/home.html&quot;)
});

but the browsers keeps displaying the following requests in the console :

GET http://localhost:8000/home [HTTP/1.1 308 Permanent Redirect 0ms]
GET http://localhost:8000/login [HTTP/1.1 200 OK 0ms]

and the server never logs anything.

I tried to remove the location.replace('/home') in the user-side code but it didn't solve the issue.

What am I missing ?

答案1

得分: 0

浏览器正在缓存您的 308 重定向。为测试目的,您可以清除浏览器缓存并禁用缓存(在 Chrome 中:检查 -> 网络标签 -> 选择禁用缓存)。

由于 Express 默认使用 HTTP 302 重定向代码,您可以将 res.redirect(308,'/login') 更改为 res.redirect('/login')

英文:

The browser is caching your 308 redirect.
For testing purposes you can clear your cache and disable caching in your browser (in Chrome: inspect -> network tab -> check disable cache)

Since Express is using HTTP code 302 for redirects by default, you could change res.redirect(308,&#39;/login&#39;) to res.redirect(&#39;/login&#39;)

huangapple
  • 本文由 发表于 2023年5月6日 18:53:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76188475.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定