将 Express.js 4 的 res.status(401) 链接到重定向

Chaining Express.js 4's res.status(401) to a redirect

如果请求用户未通过身份验证,我想发送响应代码 401,但我也想在请求是 HTML 请求时重定向。我发现 Express 4 不允许这样做:

res.status(401).redirect('/login')

有谁知道处理这个问题的方法吗?这可能不是 Express 的限制,因为我要求本质上传递两个 headers,但我不明白为什么会这样。我应该能够一次性传递 "not authenticated" 响应并重定向用户。

您当然可以在发送 401 页面的同时发送 Location: /login header,但是,这是 ill-advised,大多数浏览器不会遵循它,因为 rfc2616.

克服这个问题的一种方法是在 401 页面上同时提供 <meta http-equiv="refresh" content="0; url=/login">

res.set('Content-Type', 'text/html');
res.status(401).send('<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=/login"></head></html>');

发回新位置的方法存在一些细微差别 header。

redirect:

app.get('/foobar', function (req, res) {
  res.redirect(401, '/foo');
});
// Responds with
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Location: /foo
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 33
Date: Tue, 07 Apr 2015 01:25:17 GMT
Connection: keep-alive

Unauthorized. Redirecting to /foo

status and location:

app.get('/foobar', function (req, res) {
  res.status(401).location('/foo').end();
});
// Responds with
HTTP/1.1 401 Unauthorized
X-Powered-By: Express
Location: /foo
Date: Tue, 07 Apr 2015 01:30:45 GMT
Connection: keep-alive
Transfer-Encoding: chunked

使用 redirect 的原始(不正确)方法:

app.get('/foobar', function (req, res) {
  res.status(401).redirect('/foo')();
});
// Responds with 
HTTP/1.1 302 Moved Temporarily
X-Powered-By: Express
Location: /foo
Vary: Accept
Content-Type: text/plain; charset=utf-8
Content-Length: 38
Date: Tue, 07 Apr 2015 01:26:38 GMT
Connection: keep-alive

Moved Temporarily. Redirecting to /foo

所以看起来 redirect 将放弃任何以前的状态代码并发送默认值(除非在方法调用中指定)。这是有道理的,因为在 Express 中使用了中间件。如果您有一些全局中间件对所有请求执行 pre-checks(例如检查正确接受 header 等),它们将不知道重定向请求。然而,身份验证中间件会因此知道覆盖任何以前的设置以正确设置它们。

更新:如以下评论所述,即使 Express 可以发送具有位置 header 的 4XX 状态代码,并不意味着根据规范,请求客户端可以理解它是可接受的响应。事实上,大多数人会忽略位置 header 除非状态代码是 3XX 值。

我遇到了同样的问题,决定使用会话来处理这种工作。

我不想有一个中间视图...

使用下面的代码,我可以重定向到主页,该主页将使用 401 未经授权的代码呈现。

app.get('patternForbiddenRoute', (req, res, next) => {
       // previousCode
       if (notForbidden === true) {
           return res.render("a_view");
       }

       req.session.httpCode = 401;

       res.redirect('patternHomeRoute');
});

app.get('patternHomeRoute', (req, res, next) => {
       res.render("my_home_view", {}, (error, html) => {
            // ... handle error code ...

            const httpCode = req.session.httpCode;
            if (httpCode !== undefined) {
                delete req.session.httpCode;
                res.status(httpCode);
            }

            res.send(html);
       }));
});