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

Posted

技术标签:

【中文标题】将 Express.js 4 的 res.status(401) 链接到重定向【英文标题】:Chaining Express.js 4's res.status(401) to a redirect 【发布时间】:2015-06-11 11:13:46 【问题描述】:

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

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

有谁知道处理这个问题的方法吗?这可能不是 Express 的限制,因为我要求本质上传递两个标题,但我不明白为什么会这样。我应该能够一次性传递“未通过身份验证”的响应并重定向用户。

【问题讨论】:

我还注意到将 POST 重定向到 GET,这让我很惊讶 简短说明:如果资产可以通过身份验证访问,则状态码应为 401。如果完全禁止使用或不通过身份验证,则状态码应为 403。 @brockangelo Ixe 的回答是我知道如何做到这一点的唯一方法(手动 .set(),然后 .send())。但我认为更大的问题是,如果您计划重定向,您是否真的打算发送 401。看看我对 Jason 的回答的评论中的 SO 链接 【参考方案1】:

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

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

使用statuslocation

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 中使用了中间件。如果您有一些全局中间件对所有请求进行预检查(例如检查正确的接受标头等),他们将不知道重定向请求。但是,身份验证中间件会,因此它会知道覆盖任何以前的设置以正确设置它们。

更新:正如下面的 cmets 所述,即使 Express 可以发送带有 Location 标头的 4XX 状态代码,但这并不意味着它是请求客户端根据规范理解的可接受响应。事实上,除非状态码是 3XX 值,否则大多数人都会忽略 Location 标头。

【讨论】:

这回答了它(部分)。 redirect will abandon any previous status codes and send the default value - 注意docs here“如果你不指定状态,状态码默认为“302“找到””。但请注意,我认为 redirect 函数仅适用于 300 范围。这里有一个以前的 SO - ***.com/questions/21519094/… @JustinMaat 同意这个的整体使用是不正确的。 401 应该被发送回请求者,让他们知道他们应该使用通过 WWW-Authenticate 标头指定的正确身份验证方法再次请求。这通常是令牌或基本身份验证。我的回答是表明 Express 可以 这样做,但不是这样做是正确的。更新我的答案以反映这一点。【参考方案2】:

您当然可以在 401 页面旁边发送一个 Location: /login 标头,但是,这是不明智的,大多数浏览器都不会遵循它,就像 rfc2616 一样。

解决此问题的一种方法是将<meta http-equiv="refresh" content="0; url=/login"> 与您的401 页面一起提供:

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

【讨论】:

很抱歉恢复这个旧话题,但您的 .send() 方法中似乎有一个结束反引号而不是单引号。使用了你的好主意,但我不知道为什么会出错:) 谢谢!【参考方案3】:

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

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

使用下面的代码,我可以重定向到主页,该主页将呈现 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);
       ));
);

【讨论】:

以上是关于将 Express.js 4 的 res.status(401) 链接到重定向的主要内容,如果未能解决你的问题,请参考以下文章

错误:配置错误的 csrf - Express JS 4

express.js 4 JSON 解析来自 GET 请求的问题(从数据表请求数据)

将 Express.js 与 React.js 一起使用的推荐方式?

如何将 express.js 服务器部署到 Netlify

express.js,如果我删除下一个参数,错误处理程序将不起作用

使用 XMLHttpRequest 将多个文件上传到 Express.js 3.5 服务器