dprfe04886 2018-12-14 07:43
浏览 93
已采纳

异常时捕获请求状态

I have a Slim Framework application with a custom errorHandler, and a small middleware stack. My middleware adds attributes to the Request object which I would like to have access to from my error handler in the event of an exception. For example:

$app->get('/endpoint', function($request $response, $args) {
    $myAttribute = $request->getAttribute('myAttribute'); //returns 'myValue'
    throw new \Exception(); //any code that throws an error
})->add(function($request, $response, $next) {
    $request = $request->withAttribute('myAttribute', 'myValue');
    $response = $next($request, $response);
    return $response;
});

$app->getContainer['errorHandler'] = function($c) {
    return function($request, $response, $exception) {
        $myAttribute = $request->getAttribute('myAttribute'); //returns null
        return $response;
    }
};

The attribute does not exist within the Request object inside the error handler because the cloned Request from inside the route has not been returned after traversing through the middleware stack. Is it possible to access the Request and Response objects as they exist, at the time (in the location) the exception is thrown? I can't explicitly pass them (for example, SlimException) because I'm trying to handle unexpected errors as well.

  • 写回答

1条回答 默认 最新

  • douxin2011 2019-04-21 17:34
    关注

    I've created two, somewhat hacky, solutions to capturing Request and Response state when exceptions are thrown. Both attempt to insert a try/catch as close to the center of the middleware stack as possible (without repeating code), and involve wrapping the original exception in a new exception class together with the modified endpoint parameters.

    Middleware

    This works as long as attention is paid to the order that middleware is added. Unfortunately it does require the innermost middleware is added to each route, or at the very least, "before" the middleware modifying the Request and/or Response objects. This won't catch any changes to Request/Response made inside, nor after the route.

    class WrappedException extends \Exception {
        public $exception;
        public $request;
        public $response;
    
        public function __construct($exception, $request, $response) {
            $this->exception = $exception;
            $this->request = $request;
            $this->response = $response;
        }
    }
    
    $app->get('/endpoint', function($request $response, $args) {
        throw new \Exception(); //any code that throws an error
    })->add(function($request, $response, $next) {
        try {
            $response = $next($request, $response);
        } catch (\Exception $exception) {
            throw new WrappedException($exception, $request, $response);
        }
        return $response;
    });
    
    $app->add(function($request, $response, $next) {
        $request = $request->withAttribute('myAttribute', 'myValue');
        $response = $next($request, $response);
        return $response;
    });
    
    $app->getContainer['errorHandler'] = function($c) {
        return function($request, $response, $exception) {
            if ($exception instanceof WrappedException) {
                //returns 'myValue'
                $myAttribute = $exception->request->getAttribute('myAttribute');
            }
            return $response;
        }
    };
    

    Route Class

    This wraps all routes in a try block, and makes for a bit cleaner route code, but you must be sure to extend all routes from the RouteBase class.

    class WrappedException extends \Exception {
        public $exception;
        public $request;
        public $response;
    
        public function __construct($exception, $request = null, $response = null) {
            $this->exception = $exception;
            $this->request = $request;
            $this->response = $response;
        }
    }
    
    class RouteBase {
        public function __call($method, $arguments) {
            if (method_exists($this, $method)) {
                try {
                    $this::$method(...$arguments);
                } catch (\Exception $e) {
                    throw new WrappedException($e, ...$arguments);
                }
            } else {
                throw new \Exception('Route method not found.');
            }
        }
    }
    
    class RouteClass extends RouteBase {
        //PROTECTED -- must be callable by parent class, but not outside class
        protected function get(Request $request, Response $response, $args) {
            throw new \Exception('A new exception!');
            $response->write('hey');
            return $response;
        }
    }
    
    $app->get('/endpoint', 'RouteClass:get');
    
    $app->add(function($request, $response, $next) {
        $request = $request->withAttribute('myAttribute', 'myValue');
        $response = $next($request, $response);
        return $response;
    });
    
    $app->getContainer['errorHandler'] = function($c) {
        return function($request, $response, $exception) {
            if ($exception instanceof WrappedException) {
                //returns 'myValue'
                $myAttribute = $exception->request->getAttribute('myAttribute');
            }
            return $response;
        }
    };
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 HFSS 中的 H 场图与 MATLAB 中绘制的 B1 场 部分对应不上
  • ¥15 如何在scanpy上做差异基因和通路富集?
  • ¥20 关于#硬件工程#的问题,请各位专家解答!
  • ¥15 关于#matlab#的问题:期望的系统闭环传递函数为G(s)=wn^2/s^2+2¢wn+wn^2阻尼系数¢=0.707,使系统具有较小的超调量
  • ¥15 FLUENT如何实现在堆积颗粒的上表面加载高斯热源
  • ¥30 截图中的mathematics程序转换成matlab
  • ¥15 动力学代码报错,维度不匹配
  • ¥15 Power query添加列问题
  • ¥50 Kubernetes&Fission&Eleasticsearch
  • ¥15 報錯:Person is not mapped,如何解決?