401 перенаправляет магически приводит к перенаправлению 302

Я использую класс Redirect для отправки невостребованных пользователей на страницу входа с кодом состояния 401 :

 return Redirect::to('login', 401); 

Это отправляет правильный заголовок местоположения, но для кода состояния установлено значение 302 .


Я проследил его до базового класса Response в

Laravel / продавец / Symfony / Компонент / HttpFoundation / Response.php

и он звонит:

 $this->setStatusCode($status); 

с правильным кодом 401 .


Я также попытался сбросить объект:

 var_dump( Redirect::to('login', 401)->foundation ); 

и я вижу, что свойство statusCode правильно установлено в 401 .


Тем не менее, код статуса HTTP сгенерированного ответа установлен в 302 .

Что дает? Я использую это неправильно?


PS Я также разместил это на форумах Laravel , но безрезультатно.

Related of "401 перенаправляет магически приводит к перенаправлению 302"

Это не из-за laravel, вы можете воспроизвести это просто (php 5.4 в окнах):

 <?php header("HTTP/1.1 401 Unauthorized"); header("Location: http://www.google.com"); 

Похоже, php устанавливает значение 302:

 $ php-cgi "test.php" Status: 302 Moved Temporarily Location: http://www.google.com Content-type: text/html; charset=UTF-8 

В исходном коде PHP main / SAPI.C:

 } else if (!STRCASECMP(header_line, "Location")) { if ((SG(sapi_headers).http_response_code < 300 || SG(sapi_headers).http_response_code > 307) && SG(sapi_headers).http_response_code != 201) { /* Return a Found Redirect if one is not already specified */ if (http_response_code) { /* user specified redirect code */ sapi_update_response_code(http_response_code TSRMLS_CC); } else if (SG(request_info).proto_num > 1000 && SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD") && strcmp(SG(request_info).request_method, "GET")) { sapi_update_response_code(303 TSRMLS_CC); } else { sapi_update_response_code(302 TSRMLS_CC); } } 

Как вы можете видеть, когда вы делаете header() с "Location" , код статуса http изменяется до 302

Вы можете заставить его работать, если вы сделаете это наоборот:

 <?php header("Location: http://www.google.com"); header("HTTP/1.1 401 Unauthorized"); 

Это даст:

 $ php-cgi "test.php" Status: 401 Unauthorized Location: http://www.google.com Content-type: text/html; charset=UTF-8 

Но laravel устанавливает местоположение после установки статуса, поэтому статус все равно установлен на 302. Но это спорный вопрос, даже если вы успешно установили статус 401 с заголовком местоположения, перенаправление не сопровождается браузерами.

Браузеры не позволят вам это сделать; Вы не можете перенаправить с помощью 401.

Я следил за ним еще дальше … до public function sendHeaders() в laravel/vendor/Symfony/Component/HttpFoundation/Response.php … где он генерирует заголовок окончательного состояния

  // status Header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)); 

… и это было все еще 401, конечно.

Тогда мне стало понятно. Вы не можете перенаправить с помощью 401 , который является ClientError.

(Это также провалит тест isRedirect указанный в том же файле Response.php . mod_php исправит его для вас до 302, как только вы отправите Location (потому что по умолчанию статус 200, поэтому его необходимо обновить. PHP doesn ' t проверить, было ли оно уже обновлено на что-то еще).

Как отмечают другие, нет такой вещи, как переадресация 401. Причина, по которой я попал в это, заключалась в том, что мне нужно перенаправить пользователей, не являющихся AJAX , но запросы AJAX должны получить 401 .


Если вы оказались в подобной ситуации, вот что я в итоге использовал:

 return Request::ajax() ? Response::make('', 401, array('HTTP/1.1 401 Unauthorized')) : Redirect::to('login', 302); 

Это использует методы Laravel для обработки заголовков.


Если вы используете ванильный PHP, используйте это:

 $is_ajax_request = ! empty( $_SERVER['HTTP_X_REQUESTED_WITH'] ) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest'; $is_ajax_request ? header('HTTP/1.1 401 Unauthorized') : header('Location: http://site.com/login'); exit;