Hello Folks,
I’ve implemented Monolog on my Slim Application and I would to keep record of the Users whenever some Exception or Error occurs. ( Not only Users but some other data about the request that come from my Database ).
I have my Custom Error Handler that does all the formatting I need and trigger the logger. But I having trouble getting for example User data.
Currently there is a Middleware that retrieve user information and store it in Request Attributes.
Example: UserMiddleware
$newRequest = $request->withAttributes($userData);
$newResponse = $next($newRequest,$response);
and then on the next Route or Middleware I just have to: $request->getAttributes()['user']
and all my User data is there for use.
Lets say that UserMiddleware have completed its task without Error and moved to the Login Route and then something bad happened. That would lead to an Exception and it’ll be handler by errorHandler.
The thing is I cannot do the same in the Error Handler as its not the same $request object I think.
Example:
$this->container['errorHandler'] = function (ContainerInterface $container) {
return function (Request $request, Response $response, Exception $exception) use ($container) {
// This return an Empty Array.
$requestAttr = $request->getAttributes();
// This isn't even possible.
$userData = $requestAttr['user];
};
};
EDITED:
Currently I’m passing the Attributes to the Container everytime that setAttributes is called so that in the Handler I can retrieve all type of data.
Example: On Controller
// Just an Example.
public function setRequestAttributes($data, $replace = false){
$this->requestInstance = $this->requestInstance->withAttributes($data)
$this->getInterfaceContainer('Attributes') = $this->requestInstance->getAttributes();
}
Example: On Handler
$requestAttributes = $container->get('Attributes');
Is there any way better or safer to do that ? This is the only one I got.
Thanks,
LosLobos.
If you take a look on this code you will see how Slim handle erros.
But talking about your problem, you can just inject the request/response on your Exception
like Slim/Exception/MethodNotAllowedException (that extends SlimException) do.
Then you can do something like:
throw new MyException($request, $response, $anotherArgument, ...)
And on your error handler
$this->container['errorHandler'] = function (ContainerInterface $container) {
return function (Request $request, Response $response, Exception $exception) use ($container) {
$requestAttr = $exception->getRequest()->getAttributes();
};
};
This could be an approuch but the problem is that I dealing with multiple type of Exceptions. And if I filter the $exception using instanceof I’ll be excluding the other Exceptions and I’ll not be catching all the Errors. ( Exceptions, ArgumentInvalidExceptions, other type of exceptions… )
I think using the Container is the best idea at the moment when dealing with this situation.
Resurrecting this because I was having this exact issue.
It turns out, the order in which you add your middlewares is very important! Namely, if you have a middleware that’s adding attributes and things, and you want it available in the error middleware, you need to add it after the error middleware in the chain.
This seems kinda contrary to the template example, but I noticed that as soon as I did that the attributes were available in the error handler’s $Request object.
I’m entirely convinced I’m doing something wrong, but in v4 it seems to work this way.
Slim middleware is a stack, which means it is processed last in, first out (LIFO). So, yes, order definitely matters. However, the Error Handler should almost always be the last middleware added. This is so the error handler is setup as early as possible before your app code is ran. By putting your middleware before it in the stack, an error in your middleware will not get get handled by the Error Handler, causing unexpected behavior.
Our Solution
Create a CurrentUser
object to store the currently logged in user. Inject this into the Error Handler and Auth middleware. You set the current user in your middleware like $this->currentUser->set($user)
, and in the Error Handler, something like $userData = $currentUser->get() ?? 'unknown'
.
This works because the container injects the same shared CurrentUser
object to both.
Side note: If you are using Monolog and want the user info in the log context, you can add a Processor that injects it on every log, not just errors. You can take it a step further and only add that processor after the user is loaded. This way if an error happens during auth, it won’t try to add an empty user to the log.
Hope that helps
1 Like