Browse Source

Fixes #13707: Fixed `\yii\web\ErrorHandler` and `\yii\web\ErrorAction` not setting correct response code to response object before rendering error view

tags/2.0.12
Alexander Makarov 8 years ago committed by GitHub
parent
commit
bc59d5da85
  1. 1
      framework/CHANGELOG.md
  2. 2
      framework/web/ErrorAction.php
  3. 19
      framework/web/ErrorHandler.php
  4. 18
      framework/web/Response.php
  5. 2
      tests/data/views/error.php
  6. 12
      tests/data/views/errorHandler.php
  7. 24
      tests/framework/web/ErrorActionTest.php
  8. 45
      tests/framework/web/ErrorHandlerTest.php

1
framework/CHANGELOG.md

@ -44,6 +44,7 @@ Yii Framework 2 Change Log
- Bug #13670: Fixed alias option from console when it includes `-` or `_` in option name (pana1990)
- Enh: Added `yii\di\Instance::__set_state()` method to restore object after serialization using `var_export()` function (silvefire)
- Enh #13695: `\yii\web\Response::setStatusCode()` method now returns the Response object itself (kyle-mccarthy)
- Bug #13707: Fixed `\yii\web\ErrorHandler` and `\yii\web\ErrorAction` not setting correct response code to response object before rendering error view (samdark)
- Enh #13698: `yii\grid\DataColumn` filter is automatically generated as dropdown list in case of `format` set to `boolean` (bizley)
- Enh #13254: Core validators no longer require Yii::$app to be set (sammousa)
- Bug #4408: Add support for unicode word characters and `+` character in attribute names (sammousa, kmindi)

2
framework/web/ErrorAction.php

@ -98,6 +98,8 @@ class ErrorAction extends Action
*/
public function run()
{
Yii::$app->getResponse()->setStatusCodeByException($this->exception);
if (Yii::$app->getRequest()->getIsAjax()) {
return $this->renderAjaxResponse();
}

19
framework/web/ErrorHandler.php

@ -89,6 +89,8 @@ class ErrorHandler extends \yii\base\ErrorHandler
$response = new Response();
}
$response->setStatusCodeByException($exception);
$useErrorView = $response->format === Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);
if ($useErrorView && $this->errorAction !== null) {
@ -99,7 +101,7 @@ class ErrorHandler extends \yii\base\ErrorHandler
$response->data = $result;
}
} elseif ($response->format === Response::FORMAT_HTML) {
if (YII_ENV_TEST || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest') {
if ($this->shouldRenderSimpleHtml()) {
// AJAX request
$response->data = '<pre>' . $this->htmlEncode(static::convertExceptionToString($exception)) . '</pre>';
} else {
@ -119,12 +121,6 @@ class ErrorHandler extends \yii\base\ErrorHandler
$response->data = $this->convertExceptionToArray($exception);
}
if ($exception instanceof HttpException) {
$response->setStatusCode($exception->statusCode);
} else {
$response->setStatusCode(500);
}
$response->send();
}
@ -475,4 +471,13 @@ class ErrorHandler extends \yii\base\ErrorHandler
}
return null;
}
/**
* @return bool if simple HTML should be rendered
* @since 2.0.12
*/
protected function shouldRenderSimpleHtml()
{
return YII_ENV_TEST || isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
}
}

18
framework/web/Response.php

@ -291,6 +291,24 @@ class Response extends \yii\base\Response
}
/**
* Sets the response status code based on the exception.
* @param \Exception $e
* @throws InvalidParamException if the status code is invalid.
* @return $this the response object itself
*
* @since 2.0.12
*/
public function setStatusCodeByException(\Exception $e)
{
if ($e instanceof HttpException) {
$this->setStatusCode($e->statusCode);
} else {
$this->setStatusCode(500);
}
return $this;
}
/**
* Returns the header collection.
* The header collection contains the currently registered HTTP headers.
* @return HeaderCollection the header collection

2
tests/data/views/error.php

@ -9,6 +9,8 @@
?>
Name: <?= $name ?>
Code: <?= Yii::$app->response->statusCode ?>
Message: <?= $message ?>
Exception: <?= get_class($exception) ?>

12
tests/data/views/errorHandler.php

@ -0,0 +1,12 @@
<?php
/**
* @var Exception $exception
*/
?>
Code: <?= Yii::$app->response->statusCode ?>
Message: <?= $exception->getMessage() ?>
Exception: <?= get_class($exception) ?>

24
tests/framework/web/ErrorActionTest.php

@ -7,7 +7,6 @@ use yii\base\InvalidConfigException;
use yii\base\UserException;
use yii\web\Controller;
use yii\web\ErrorAction;
use yii\web\Request;
use yiiunit\TestCase;
/**
@ -18,16 +17,15 @@ class ErrorActionTest extends TestCase
protected function setUp()
{
parent::setUp();
$this->mockApplication([
'components' => [
'request' => [
'class' => Request::className(),
],
],
]);
$this->mockWebApplication();
}
/**
* Creates a controller instance
*
* @param array $actionConfig
* @return TestController
*/
public function getController($actionConfig = [])
{
return new TestController('test', Yii::$app, ['layout' => false, 'actionConfig' => $actionConfig]);
@ -38,6 +36,7 @@ class ErrorActionTest extends TestCase
Yii::$app->getErrorHandler()->exception = new InvalidConfigException('This message will not be shown to the user');
$this->assertEquals('Name: Invalid Configuration
Code: 500
Message: An internal server error occurred.
Exception: yii\base\InvalidConfigException', $this->getController()->runAction('error'));
}
@ -47,6 +46,7 @@ Exception: yii\base\InvalidConfigException', $this->getController()->runAction('
Yii::$app->getErrorHandler()->exception = new UserException('User can see this error message');
$this->assertEquals('Name: Exception
Code: 500
Message: User can see this error message
Exception: yii\base\UserException', $this->getController()->runAction('error'));
}
@ -63,6 +63,7 @@ Exception: yii\base\UserException', $this->getController()->runAction('error'));
Yii::$app->getErrorHandler()->exception = new \InvalidArgumentException('This message will not be shown to the user');
$this->assertEquals('Name: Error
Code: 500
Message: An internal server error occurred.
Exception: InvalidArgumentException', $this->getController()->runAction('error'));
}
@ -77,6 +78,7 @@ Exception: InvalidArgumentException', $this->getController()->runAction('error')
]);
$this->assertEquals('Name: Oops...
Code: 500
Message: The system is drunk
Exception: InvalidArgumentException', $controller->runAction('error'));
}
@ -84,6 +86,7 @@ Exception: InvalidArgumentException', $controller->runAction('error'));
public function testNoExceptionInHandler()
{
$this->assertEquals('Name: Not Found (#404)
Code: 404
Message: Page not found.
Exception: yii\web\NotFoundHttpException', $this->getController()->runAction('error'));
}
@ -95,7 +98,8 @@ Exception: yii\web\NotFoundHttpException', $this->getController()->runAction('er
// Unset view name. Class should try to load view that matches action name by default
$action->view = null;
$this->setExpectedExceptionRegExp('yii\base\ViewNotFoundException', '#The view file does not exist: .*?views/test/error.php#');
$ds = preg_quote(DIRECTORY_SEPARATOR, '\\');
$this->setExpectedExceptionRegExp('yii\base\ViewNotFoundException', '#The view file does not exist: .*?views' . $ds . 'test' . $ds . 'error.php#');
$this->invokeMethod($action, 'renderHtmlResponse');
}

45
tests/framework/web/ErrorHandlerTest.php

@ -0,0 +1,45 @@
<?php
namespace yiiunit\framework\web;
use Yii;
use yii\web\NotFoundHttpException;
use yiiunit\TestCase;
class ErrorHandlerTest extends TestCase
{
protected function setUp()
{
parent::setUp();
$this->mockWebApplication([
'components' => [
'errorHandler' => [
'class' => 'yiiunit\framework\web\ErrorHandler',
'errorView' => '@yiiunit/data/views/errorHandler.php',
],
],
]);
}
public function testCorrectResponseCodeInErrorView()
{
/** @var ErrorHandler $handler */
$handler = Yii::$app->getErrorHandler();
$this->invokeMethod($handler, 'renderException', [new NotFoundHttpException('This message is displayed to end user')]);
$out = Yii::$app->response->data;
$this->assertEquals('Code: 404
Message: This message is displayed to end user
Exception: yii\web\NotFoundHttpException', $out);
}
}
class ErrorHandler extends \yii\web\ErrorHandler
{
/**
* @return bool if simple HTML should be rendered
*/
protected function shouldRenderSimpleHtml()
{
return false;
}
}
Loading…
Cancel
Save