Chaining Express.js 4 res.status(401) для перенаправления

Я бы хотел отправить код ответа 401, если запрашивающий пользователь не прошел аутентификацию, но я также хотел бы перенаправить, когда запрос был запросом HTML. Я обнаружил, что Express 4 не позволяет этого:

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

Кто-нибудь знает, как справиться с этим? Это может быть не ограничение Express, так как я прошу существенно передать два заголовка, но я не понимаю, почему так должно быть. Я должен иметь возможность передать "не аутентифицированный" ответ и перенаправить пользователя за один раз.

Ответ 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

С status и 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. Если у вас было какое-то глобальное промежуточное программное обеспечение, выполняющее предварительную проверку всех запросов (например, проверка правильности принятых заголовков и т.д.), Они не знали бы перенаправить запрос. Однако промежуточное ПО аутентификации будет и, следовательно, будет знать, чтобы переопределить любые предыдущие настройки, чтобы правильно их установить.

ОБНОВЛЕНИЕ: Как указано в комментариях ниже, даже несмотря на то, что Express может отправлять код состояния 4XX с заголовком Location, это не означает, что это приемлемый ответ для клиента запроса, чтобы понять его в соответствии со спецификациями. Фактически большинство игнорирует заголовок Location, если код состояния не является значением 3XX.

Ответ 2

Вы, конечно, можете отправить заголовок Location: /login вместе со своей страницей 401, однако это не рекомендуется, и большинство браузеров не будут следовать ему, как указано в 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>');

Ответ 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);
       }));
});