9

我想做一些与非常相似的事情,但在 CakePHP 世界中用于 AJAX 请求。目前我正在这样做:

$this->autoRender = false;
$this->response->statusCode(500);

它基于。但是,此解决方案不允许我像 Rails 示例中那样包含自定义消息,因此,在我的客户端错误处理程序中,我可以显示 500 错误响应中包含的消息。

我将如何在 CakePHP 中实现与 Ruby on Rails 示例相同的功能?

4

3 回答 3

7

如上所述,异常是在 CakePHP 中对 AJAX 请求返回错误的方式。这是我更好地控制错误外观的解决方案。另外,如上所述,我使用的是自定义异常渲染器,但不是自定义异常。默认错误响应是这样的 JSON 对象:

{"name":"An Internal Error Has Occurred", "url": "\/users\/login.json"}

几乎喜欢默认渲染器处理 AJAX 错误的方式;我只想稍微调整一下:

<?php
// File: /app/Lib/Error/CustomExceptionRenderer.php
App::uses('ExceptionRenderer', 'Error');
class CustomExceptionRenderer extends ExceptionRenderer {

    // override
    public function error400($error) {
        $this->_prepareView($error, 'Not Found');
        $this->controller->response->statusCode($error->getCode());

        $this->_outputMessage('error400');
    }

    // override
    public function error500($error) {
        $this->_prepareView($error, 'An Internal Error Has Ocurred.');
        $code = ($error->getCode() > 500 && $error->getCode() < 506) ? $error->getCode() : 500;
        $this->controller->response->statusCode($code);

        $this->_outputMessage('error500');
    }

    private function _prepareView($error, $genericMessage) {
        $message = $error->getMessage();
        if(!Configure::read('debug') && !Configure::read('detailed_exceptions')) {
            $message = __d('cake', $genericMessage);
        }
        $url = $this->controller->request->here();
        $renderVars = array(
            'name' => h($message),
            'url' => h($url),
            );
        if(isset($this->controller->viewVars['csrf_token'])) {
            $renderVars['csrf_token'] = $this->controller->viewVars['csrf_token'];
        }
        $renderVars['_serialize'] = array_keys($renderVars);
        $this->controller->set($renderVars);
    }
}

然后,在 bootstrap.php 中:

Configure::write('Exception.renderer', 'CustomExceptionRenderer');

所以这是它的工作原理:

  • 假设我想在我的错误响应中返回一个新的 CSRF 令牌,这样如果我现有的令牌在抛出异常之前就已经过期,那么下次我尝试请求时就不会陷入黑洞。查看安全组件文档以了解更多关于 CSRF 保护的信息。
  • Create a new class in app/Lib/Error. You can extend the default renderer, or not. Since I just want to change a few small things, and to keep the example simple, I'm extending it.
  • Override the methods that the default renderer uses to create the JSON object that will be returned. This is done with via the Request Handler Component, and conforms to best practices. Indeed, the default renderer does the same thing.
  • New private method to keep things DRY.
  • My solution to the problem of not getting custom error messages in production is to add an optional configuration key. By default this class will show the generic messages in production, but if you have debug set to 0, and you want the specific error messages: Configure::write('detailed_exceptions', 1);
  • Add the new token to the response if it exists. In my case, I have already called Controller::set on the new token in the beforeFilter method of AppController, so it is available in $this->controller->viewVars. There are probably dozens of other ways of accomplishing this.

Now your response looks like this:

{
    "name":"The request has been black-holed",
    "url":"\/users\/login.json",
    "csrf_token":"1279f22f9148b6ff30467abaa06d83491c38e940"
}

Any additional data, of any type can be added to the array passed to Controller::set for the same result.

于 2013-07-10T18:58:46.440 回答
2

在使用 ajax 请求(在我的例子中是 jquery mobile)时,我也遇到了自定义异常和错误代码。这是我想出的解决方案,不涉及覆盖调试模式。它在开发模式下抛出自定义错误,也可以在生产模式下抛出自定义错误。我希望它可以帮助某人:

AppExceptionRenderer.php:

<?php
App::uses('ExceptionRenderer', 'Error');

class AppExceptionRenderer extends ExceptionRenderer 
{
    public function test($error) 
    {   
        $this->_sendAjaxError($error);
    }

    private function _sendAjaxError($error)
    {
        //only allow ajax requests and only send response if debug is on
        if ($this->controller->request->is('ajax') && Configure::read('debug') > 0)
        {
            $this->controller->response->statusCode(500);
            $response['errorCode'] = $error->getCode();
            $response['errorMessage'] = $error->getMessage();
            $this->controller->set(compact('response'));
            $this->controller->layout = false;
            $this->_outputMessage('errorjson');
        }
    }
}

Configure::read('debug') > 0如果要在调试模式下显示异常,可以省略。视图errorjson.ctp位于 'Error/errorjson.ctp' 中:

<?php
echo json_encode($response);
?>

在这种情况下,我的异常被称为

测试异常

并定义如下:

<?php
class TestException extends CakeException {
    protected $_messageTemplate = 'Seems that %s is missing.';

    public function __construct($message = null, $code = 2) {
         if (empty($message)) {
                     $message = 'My custom exception.';
           }
           parent::__construct($message, $code);
    }
}

我的 json 响应有一个自定义错误代码 2 $code = 2,。ajax 响应将使用以下 json 数据投射错误 500:

{"errorCode":"2","errorMessage":"My custom exception."}

显然,您还需要从控制器中抛出异常:

throw new TestException();

并包括异常渲染器http://book.cakephp.org/2.0/en/development/exceptions.html#using-a-custom-renderer-with-exception-renderer-to-handle-application-exceptions

这可能有点超出范围,但为了处理 JQuery 中的 ajax 错误响应,我使用:

$(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
    //deal with my json error
});
于 2013-01-14T12:45:18.433 回答
0

您可以按照 Cookbook 中的说明使用 CakeExceptions:http ://book.cakephp.org/2.0/en/development/exceptions.html 但是debug = 1如果您想使用自定义消息,我发现除了在您的生产模式中使用之外别无他法: (

这是我使用内置方法的方法:

在您的控制器中:

if($this->request->is('ajax')){
    Configure::write('debug', 1);
}

if(!$allowed) {
    throw new InternalErrorException('Keep your fingers away from me!'); // 500 error
}

将错误模板更改为在 AJAX 调用中使用时只输出错误/app/View/Errors/error500.ctp:

<?php
if($this->request->is('ajax')):
    // Output for AJAX calls
    echo $name;

else:
    //Standard CakePHP output ?>
    <h2><?php echo $name; ?></h2>
    <p class="error">
        <strong><?php echo __d('cake', 'Error'); ?>: </strong>
        <?php echo __d('cake', 'An Internal Error Has Occurred.'); ?>
    </p>
    <?php
    if (Configure::read('debug') > 0 ):
        echo $this->element('exception_stack_trace');
    endif;

endif; ?>

然后,您可以在 AJAX 中解析返回的文本。这是我使用的 jQuery 部分:

//...
error: function (request) {
    yourErrorShowingFunction(_this, request.responseText);
}
//...

希望这可以帮助 :)

如果有人知道如何在生产模式下使用自定义错误(不覆盖调试模式),我会非常高兴!

于 2012-08-03T11:22:13.930 回答