CakePHP 中的 Ajax 错误处理

Posted

技术标签:

【中文标题】CakePHP 中的 Ajax 错误处理【英文标题】:Ajax error handling in CakePHP 【发布时间】:2012-07-20 02:24:30 【问题描述】:

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

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

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

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

【问题讨论】:

【参考方案1】:

如上所述,异常是在 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 令牌,这样如果我现有的令牌在引发异常之前已经过期,那么我下次尝试请求时就不会陷入黑洞。查看Security Component documentation 了解有关 CSRF 保护的更多信息。 在 app/Lib/Error 中创建一个新类。您可以扩展默认渲染器,也可以不扩展。由于我只想更改一些小东西,并且为了保持示例简单,我正在扩展它。 覆盖默认渲染器用于创建将返回的 JSON 对象的方法。这是通过Request Handler Component 完成的,并且符合最佳实践。事实上,默认渲染器也做同样的事情。 保持干燥的新私有方法。 我对在生产中没有收到自定义错误消息的问题的解决方案是添加一个可选的配置键。默认情况下,此类将显示生产中的通用消息,但如果您将调试设置为 0,并且您想要特定的错误消息:Configure::write('detailed_exceptions', 1); 将新令牌添加到响应中(如果存在)。在我的例子中,我已经在 AppController 的 beforeFilter 方法中的新令牌上调用了Controller::set,所以它在$this-&gt;controller-&gt;viewVars 中可用。可能还有很多其他方法可以实现这一点。

现在您的回复如下所示:


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

任何类型的任何附加数据都可以添加到传递给Controller::set 的数组中以获得相同的结果。

【讨论】:

这对我帮助很大。【参考方案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') &gt; 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
);

【讨论】:

效果很好。谢谢。【参考方案3】:

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

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

在你的控制器中:

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


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

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

<?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);

//...

希望这会有所帮助:)

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

【讨论】:

尽管这不是我希望的答案,因为它涉及调试模式,但我会将其标记为正确,因为似乎没有其他人对此有任何想法。感谢您的努力!

以上是关于CakePHP 中的 Ajax 错误处理的主要内容,如果未能解决你的问题,请参考以下文章

PHP CakePHP的Google Ajax Libraries API处理程序

统一处理jquery ajax请求过程中的异常错误信息的机制

与 ASP.net MVC 中 Ajax 请求中的错误处理有关的疑问

CakePHP4 Ajax:从控制器发送到视图

CakePHP 3.5 400 错误请求

CakePHP Auth 登录(错误的方法?)