异常时捕获请求状态

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.

doudi1978
doudi1978 我试图处理所有异常,而不仅仅是我自己抛出的异常,因为我试图在我的问题的最后一句中解释。
一年多之前 回复
drpsrvu85668
drpsrvu85668 你应该创建自己的Exception,类似于WithLastRequestException,它将Request作为构造函数+方法的参数,以getLastRequest()............抛出新的WithRequestException('somemessage',0,null,$请求);然后在错误处理程序if($exceptioninstanceofWithRequestException){$exception->getLastRequest();//做你的东西}
接近 2 年之前 回复
doutan1875
doutan1875 我试图说,如果有一种机制以某种方式确定异常处理程序中最新创建的请求对象,可以使用相同的机制在中间件中执行相同的操作,并且您无需显式传递请求对象。所以我认为如果异常处理程序REALLY需要访问请求对象(我猜它没有,它可能只需要一些属性)你需要显式地传递给它。我想不出任何自动方法,因为Slim不跟踪创建的对象,但你可以做到。
接近 2 年之前 回复
duanlao1552
duanlao1552 我理解这个概念,并且我将“新”Request对象传递给每个中间件层。但是,当抛出异常时,它会被捕获,并且“新”对象不会自动返回到该点。当触发try块时,对Request和Response对象的任何“更改”都会被忽略,因为它们处于完全不同的范围内,并且不会自动捕获或传递到任何地方。
接近 2 年之前 回复
dtutlamjasblef7982
dtutlamjasblef7982 消息对象是不可变的,对它们的每次修改都会导致创建一个新对象,这就是为什么你必须显式覆盖像$request=$request->withAttri...这样的对象并将修改后的版本传递给下一个中间件,所以它有点不清楚说它们存在。你实际上可以明确地传递它们,我说你应该,因为这些数据与这个特定的异常有关,而不是所有其他异常。您不必以相同的方式处理所有错误,可以不同的方式处理不同的异常,因此不必担心其他类型的意外错误。
接近 2 年之前 回复
doufan9290
doufan9290 虽然不是唯一的情况,但我主要是尝试获取各种OAuth属性,如下所述:oauth2.thephpleague.com/resource-server/securing-your-api/...
接近 2 年之前 回复
dongyonglie5132
dongyonglie5132 答案可能是,不,你不能轻易做到这一点。请注意,withAttribute()用于注入属性,如PSR-7中的getAttributes()所述。您的示例中的myAttribute是否符合该描述?如果是这样,可能会有针对您的问题的解决方法,如果没有,您可能需要避免使用withAttribute()并考虑更好的方法。
接近 2 年之前 回复
dousi4257
dousi4257 你猜我可以访问$_REQUEST超全局。
接近 2 年之前 回复

1个回答

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;
    }
};
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐