I am trying to trap TokenMismatchException in Laravel’s Handler.php
When I mimic a csrf token exception by temporarily removing the token from the form, the local dev version of my site shows me:
TokenMismatchException in VerifyCsrfToken.php line 68:
But when I change the render() function in Handler.php to look for the exception and handle the error, then it doesn’t work. For instance, if I replace the default code with the below for testing, and take the csrf token from the form, the system returns my 'this was not a token problem' message, and not the 'token problem' message.
public function render($request, Exception $exception)
{
if($exception instanceof TokenMismatchException) {
return('token problem');
}else{
return('this was not a token problem');
}
return parent::render($request, $exception);
}
So, with the default code Laravel seems to recognize the TokenMismatchException, but with my simple test code above, it doesn’t. Can you explain to me what’s going on here?
Chances are it's crashing because the return is expecting a \Illuminate\Http\Response from render()
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
if($exception instanceof TokenMismatchException)
return response()->json('Token mismatch');
return parent::render($request, $exception);
}
Remember to use the correct class for the Exception
use Illuminate\Session\TokenMismatchException;
Related
Laravel Version: 5.5.14
PHP Version: 7.1.10
Description:
When I try to POST with X-Requested-With='XMLHttpRequest' without CSRF-TOKEN I receive null message in response.
I add this code to App\Exceptions\Handler:
public function render($request, Exception $exception)
{
if($exception instanceof TokenMismatchException) {
abort(419, 'Token Mismatch OR page has expired due to inactivity.');
}
return parent::render($request, $exception);
}
Now I have Token Mismatch OR page has expired due to inactivity. message in response, but status text is: unknown status.
How can I set status text?
Return a \Illuminate\Http\Response and manually set the status code:
use Illuminate\Http\Response;
// snip
public function render($request, Exception $exception)
{
if($exception instanceof TokenMismatchException) {
return (new Response)->setStatusCode(419, "Token Mismatch OR page has expired due to inactivity.");
}
return parent::render($request, $exception);
}
The Status Code is part of the HTTP Protocol. 419 is not an official status Code see: https://en.wikipedia.org/wiki/List_of_HTTP_status_codes therefor it is unknown.
So unknown status is right.
But if you really looking for the file where this is defined, go to:
/vendor/symfony/http-foundation/Response.php
Note:
If you change this file, than you have to do this after every update again.
In an Extbase extension, the need may arise to inform the user about an error or exception.
In my case, I have to parse some data from a potentially bad source. So the extension has to validate this data. And if the data is invalid, it needs to throw an exception that then can be handled by TYPO3.
However, I can only find information about how the exception and error handlers works, but no information on how to correctly throw an exception from inside an extension.
So what is the intended way to throw an exception from inside an Extbase extension?
Expected result
If I produce a syntax error, TYPO3 displays a message similar to this:
(Taken from the core API reference.)
That is what I would expect a correctly thrown error or exception to look like.
What I tried
Edit: I tried throwing an error like this:
throw new \Exception('Invalid data');
However, all the frontend displays is
Oops, an error occurred! Code: 20160721101726b5339896
Another possible way to produce an error:
$GLOBALS['TSFE']->pageNotFoundAndExit('Invalid data');
However, this shows a Page Not Found error instead of the expected exception.
You implicitly asked 2 questions:
How do I correctly throw an exception in my code?
I think, that was correct, what you did: Just use PHP \Exception or a suitable exception inherited from \Exception:
throw new \UnexpectedValueException('Invalid data');
Once an exception has been thrown, how do I see more information?
This has already been answered quite well: https://stackoverflow.com/a/34067853/2444812
On a development system:
set configuration preset "debug"
Add TypoScript on start page: config.contentObjectExceptionHandler = 0
see Error and ExceptionHandling Example
On a production system:
You usually do not want to see full stack traces in the frontend. That is why config.contentObjectExceptionHandler is usually set to default, which only shows Oops, an error occurred! Code: 20160721101726b5339896 on the rendered page. Using this code, you can look in the logs (if things are logged and what is logged where always depends on the configuration of the logging system):
sys_log : see "Log" in the backend
logfile: var/logs/*.log (see Logging with TYPO3). May be typo3temp/logs on older versions and typo3temp/var/logs/ on non-Composer systems.
namespace VendorName\ExtensionName\Controller;
abstract class ActionController extends \TYPO3\CMS\Extbase\Mvc\Controller\ActionController {
/**
* #var string
*/
protected $entityNotFoundMessage = 'The requested entity could not be found.';
/**
* #var string
*/
protected $unknownErrorMessage = 'An unknown error occurred. The wild monkey horde in our basement will try to fix this as soon as possible.';
/**
* #param \TYPO3\CMS\Extbase\Mvc\RequestInterface $request
* #param \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response
* #return void
* #throws \Exception
* #override \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
*/
public function processRequest(\TYPO3\CMS\Extbase\Mvc\RequestInterface $request, \TYPO3\CMS\Extbase\Mvc\ResponseInterface $response) {
try {
parent::processRequest($request, $response);
}
catch(\Exception $exception) {
// If the property mapper did throw a \TYPO3\CMS\Extbase\Property\Exception, because it was unable to find the requested entity, call the page-not-found handler.
$previousException = $exception->getPrevious();
if (($exception instanceof \TYPO3\CMS\Extbase\Property\Exception) && (($previousException instanceof \TYPO3\CMS\Extbase\Property\Exception\TargetNotFoundException) || ($previousException instanceof \TYPO3\CMS\Extbase\Property\Exception\InvalidSourceException))) {
$GLOBALS['TSFE']->pageNotFoundAndExit($this->entityNotFoundMessage);
}
throw $exception;
}
}
/**
* #return void
* #override \TYPO3\CMS\Extbase\Mvc\Controller\ActionController
*/
protected function callActionMethod() {
try {
parent::callActionMethod();
}
catch(\Exception $exception) {
// This enables you to trigger the call of TYPO3s page-not-found handler by throwing \TYPO3\CMS\Core\Error\Http\PageNotFoundException
if ($exception instanceof \TYPO3\CMS\Core\Error\Http\PageNotFoundException) {
$GLOBALS['TSFE']->pageNotFoundAndExit($this->entityNotFoundMessage);
}
// $GLOBALS['TSFE']->pageNotFoundAndExit has not been called, so the exception is of unknown type.
\VendorName\ExtensionName\Logger\ExceptionLogger::log($exception, $this->request->getControllerExtensionKey(), \VendorName\ExtensionName\Logger\ExceptionLogger::SEVERITY_FATAL_ERROR);
// If the plugin is configured to do so, we call the page-unavailable handler.
if (isset($this->settings['usePageUnavailableHandler']) && $this->settings['usePageUnavailableHandler']) {
$GLOBALS['TSFE']->pageUnavailableAndExit($this->unknownErrorMessage, 'HTTP/1.1 500 Internal Server Error');
}
// Else we append the error message to the response. This causes the error message to be displayed inside the normal page layout. WARNING: the plugins output may gets cached.
if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) {
$this->response->setStatus(500);
}
$this->response->appendContent($this->unknownErrorMessage);
}
}
}
Here is an article that explains that.
However like most of articles about TYPO3 Programming is in German ;-)
http://nerdcenter.de/extbase-fehlerbehandlung/
So I want to cover all posible and unexpected errors (like 401, 404, 500) with just one view. I want the same view to show up on all possible errors. I came up with a solution - to copy/paste the same code and just name the views with different error codes. But that seems stiff and wrong. Is there a better way of achieving this?
In the file app/Exceptions/Handler.php you can change what happens when an exception is thrown. In particular there's a render method in there that you can use to catch all the exceptions in an application.
public function render($request, Exception $e)
{
// Handle your error here. Perhaps you can show a view
// when a condition is met. Anything that isn't caught
// here will be handled by Laravel with the
// parent::render call below
return parent::render($request, $e);
}
The parent::render($request, $e) is where Laravel would normally show it's exception/oops page. So by overriding this method you can catch all application errors, including 404, 401 etc.
A cleaner way to achieve this effect is by modifying Laravel's exception handler.
Modify App\Exceptions\Handler to catch every error and return your shared custom error page.
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $e
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $e)
{
if ($e instanceof NotFoundHttpException) {
return response()->view('errors.custom', [], 404);
}
return parent::render($request, $e);
}
Some customization may be required to fully meet exactly what & how you want data passed to your shared custom view.
Pass the error code to your view on the handler, and display the code on your page, use a switch to handle all the messages depending on the error code.
You can create one unique view (default to 404 error), use try catch in your code to capture other errors and call to this view with parameters so you can change the 404 default error to other error.
I am trying to implement exception handling in my application. For this Laravel framework has its own mechanism to handle the exception using report and render method. But to implement exception I need to track the source from where the exception has been raised e.g. specific page, route etc. For this I need to pass the url to report and render method but unable to do so. What needs to be done in order to implement this in below report and render function.
public function report(Exception $e)
{
parent::report($e);
}
public function render($request, Exception $e)
{
/* Token mismatch Exception handler start */
if ($e instanceof \Illuminate\Session\TokenMismatchException) {
return response()->view('errors.sessionExpire', [], 500);
}
/* Token mismatch Exception handler start */
return parent::render($request, $e);
}
As you can see from your own example, you have an instance of Request in the argument list. And Request has all request-specific details like current route, URL and so on.
$request->url(); // Current request URL
$request->fullUrl(); // With query parameters
$request->route(); // Get the route closure for this request path
You can also create your own exception classes that accept as many parameters as you wish!
And the less comfortable way already mentioned – you could go through the exception trace.
You need to use Exception::getTrace
var_dump($e->getTrace());
above line will give you all details regarding exception.
public function report(Exception $e){
echo '<pre>'; // add this line
print_r($e->getTrace()); // add this line
parent::report($e);
}
I am on Laravel 5.1.
Whenever there is an exception, Laravel usually shows an exception on the page. I'd like to add a custom 404 page with a human readable error report and email an admin with the entire error dump. I would also like to have the email nicely formatted in HTML (like on the error page).
How can I do this?
So, the first part for displaying fancy 404 comes with adding 404.blade.php to resources/views/errors.
Regarding emailing the error_dump, this requires a way to catch the error events. Here's how to handle that. In the file App\Exceptions\Handler.php, add the email code to your report() method
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* #param \Exception $e
* #return void
*/
public function report(Exception $e)
{
if ($e instanceof NotFoundHttpException) {
//Send email here
}
return parent::report($e);
}