Get ValidationException in customer Middelware after the Response [response->exception = null] - php

I want to modify the Errors fields when I have ValidationException. The problem is when I have ValidationException, the field exception of response variable give me null.
Part of handle function
dd($response);
if(isset($response->exception) && $response->exception instanceof validationException){
$data = $response->getData();
$transformedErros = array();
foreach ($data->error as $field => $value) {
$transformedField = $transformer::transformedAttribute($field);
$transformedErros[$transformedField] = str_replace($field, $transformedField, $value);
}
$data->error = $transformedErros;
$response->setData($data);
}

It's probably because the exception handler already has handled the exception.
In any case. You error handling should not live in your middleware but in your exception handler. You can register your own exception handler in bootstrap/app.php with below code:
$app->singleton(
Illuminate\Contracts\Debug\ExceptionHandler::class,
App\Exceptions\Handler::class
);
Let App\Exceptions\Handler extend \Laravel\Lumen\Exceptions\Handler and override what you need to change.
Take a look at https://laravel.com/docs/5.8/errors#the-exception-handler to learn more about exception handlers.

Related

Replacement for notFoundHandler setting

I'm migrating from Slim/3 to Slim/4. I've found or figured out replacements for all the features I was using that have been removed, except 404 Not Found Handler (part of the now gone App::$settings):
Slim App::$settings have been removed, multiple middleware have been implemented to replace the functionality from each individual settings.
Is there a middleware for notFoundHandler? If there isn't, how can I implement it?
Mine used to look like this:
use Slim\Container;
$config = new Container();
$config['notFoundHandler'] = function (Container $c) {
return function (Request $request, Response $response) use ($c): Response {
$page = new Alvaro\Pages\Error($c);
return $page->notFound404($request, $response);
};
};
According to Slim 4 documentation on error handling
Each Slim Framework application has an error handler that receives all
uncaught PHP exceptions
You can set a custom error handler to handle each type of exceptions thrown. A list of predefined exception classes is available on same page.
Here is a very basic example of how to register a closure as an error handler, to handle only HttpNotFoundException exceptions. You can also put the handler in a class that extends Slim\Handlers\ErrorHandler. Also, I did not actually use your Alvaro\Pages\Error to generate the response, but changing it should be straightforward:
<?php
require '../vendor/autoload.php';
$app = Slim\Factory\AppFactory::create();
// Define Custom Error Handler
$customErrorHandler = function (
Psr\Http\Message\ServerRequestInterface $request,
\Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app) {
$response = $app->getResponseFactory()->createResponse();
// seems the followin can be replaced by your custom response
// $page = new Alvaro\Pages\Error($c);
// return $page->notFound404($request, $response);
$response->getBody()->write('not found');
return $response->withStatus(404);
};
// Add Error Middleware
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
// Register the handler to handle only HttpNotFoundException
// Changing the first parameter registers the error handler for other types of exceptions
$errorMiddleware->setErrorHandler(Slim\Exception\HttpNotFoundException::class, $customErrorHandler);
$app->get('/', function ($request, $response) {
$response->getBody()->write('Hello Slim 4');
return $response;
});
$app->run();
Another approach is to create a generic error handler and register it as the default handler, and inside that handler, decide what response should be sent based on type of exception that is thrown. Something like:
$customErrorHandler = function (
Psr\Http\Message\ServerRequestInterface $request,
\Throwable $exception,
bool $displayErrorDetails,
bool $logErrors,
bool $logErrorDetails
) use ($app) {
$response = $app->getResponseFactory()->createResponse();
if ($exception instanceof HttpNotFoundException) {
$message = 'not found';
$code = 404;
} elseif ($exception instanceof HttpMethodNotAllowedException) {
$message = 'not allowed';
$code = 403;
}
// ...other status codes, messages, or generally other responses for other types of exceptions
$response->getBody()->write($message);
return $response->withStatus($code);
};
Then you can set this as the default error handler:
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$errorMiddleware->setDefaultErrorHandler($customErrorHandler);

Laravel custom exception

Before posting this question I have searched internet for appropriate answers but got none.These are my following questions:
1) How to throw exception without try catch in laravel controller and get exception in the view of the called controller.
Example : TestController.php
function index(){
throw new CustomException('Data not found');
return view('dashboard');
}
How to get exception message in dashboard view
2) How to set format for exception message, suppose I want to return format as
$response['code'] = 0;
$response['status'] = 'error';
$response['message'] = 'message';
$response['data'] = '';
I have created a custom exception but don't know how to use it to fully
<?php
namespace App\Exceptions;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Mockery\Exception;
class CustomException extends Exception{
public $response;
/**
* Report the exception.
*
* #return void
*/
public function report()
{
}
/**
* Render the exception into an HTTP response.
*
* #param \Illuminate\Http\Request
* #return \Illuminate\Http\Response
*/
public function render($request,$exception){
ob_clean();
$response['code'] = 0;
$response['status'] = 'error';
$response['message'] = 'Message';
$response['data'] = '';
if(!$request->ajax()){
// non ajax response
}else{
return response()->json($response);
}
}
}
Answering your questions:
To pass this exception to view, you can implement render method what you already started to do. You can just do:
if(!$request->ajax()){
view('error_handler', compact('exception'))
} else {
return response()->json($response);
}
so now you can just create error_handler.blade.php view and you will have access in there to $exception variable, so you can use in there {{ $exception->getMessage}} and so on
You didn't define what exactly you want to achieve, however it should work without any problem:
public function render($request,$exception) {
if(!$request->ajax()){
view('error_handler', compact('exception'))
}
return response()->json([
'code' => $exception->getCode(),
'status' => 'error',
'message' => $exception->getMessage(),
'data' => 'sample data'
]);
}
Of course instead of using $exception->getCode() for code you can put anything you want, this is just an example that you can also use code and message that you have in your exception assuming you set some custom when you throw exception for example:
throw new CustomException('This is custom message', 123);
There isn't realy reason to thrown an exception if your purpose is to show the message of that exception within the view which is rendered by the controller. And It isn't a best way to manage exception, because by default all exception which are throw are handle and catch within the App\Exceptions\Handler class.
I think you know exactly when that type of CustomExption you have create will be thrown, but instead of throwing that error just traite that error which need an exception in different way without exception. For that you can create an Array in which old code, status, message, data and pass it to the view method;
class CustomController extends Controller
{
public function samemethod(){
// some code that can generate an error
// construct the error data
$customErrorData = [
'code' => 0000,
'status' => "some status",
'message' => "Some error message",
'data' => [...]
];
// After error data creation you can pass it to the view
return View::make('customview', compact('customErrorData'));
}
}
You have you error data in your view
All uncaught exceptions are intercepted by default exceptions handler. If you want this to behave differently, you just need to modify it: https://laravel.com/docs/5.7/errors#the-exception-handler

How can I intercept exceptions in ZendFramework 3

I'm using ZendFramework 3 in my REST API project. So there are few modules and a plugin which checks an authorization status. If the authorization fails it throws an Exception.
There is no way to handle it in each controller separately using try .. catch. How can I intercept and handle the Exception and generate JSON output like this?
{
message: "Access denied",
reason: "Your token is incorrect"
}
I'm a newbie in ZendFramework, that's why I have no idea how to do this. And official documentation didn't say a word about this.
There are default framework Events that are triggered including the event MvcEvent::EVENT_DISPATCH_ERROR. So, all you should do is to attach listener on that error event and return JSON response.
First, you need to register your Listener in module.config.php
// In my case module name is Api
'listeners' => [
Api\Listener\ApiListener::class // Register the class listener
],
'service_manager' => [
'invokables' => [
// Register the class (of course you can use Factory)
Api\Listener\ApiListener::class => Api\Listener\ApiListener::class
],
],
Second, create the file class Api/Listener/ApiListener.php
<?php
namespace Api\Listener;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;
use Zend\Mvc\MvcEvent;
use Zend\Console\Request as ConsoleRequest;
use Zend\View\Model\JsonModel;
class ApiListener extends AbstractListenerAggregate
{
public function attach(EventManagerInterface $events, $priority = 1)
{
// Registr the method which will be triggered on error
$this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH_ERROR,
[$this, 'handleError'], 0);
}
/**
* Return JSON error on API URI(s)
*/
public function handleError(MvcEvent $e)
{
$request = $e->getParam('application')->getRequest();
if($request instanceof ConsoleRequest){
return;
}
//If you want to convert Response only on some URIs
//$uri = $request->getUri()->getPath();
//if(0 !== strpos($uri, '/api')){
// return;
//}
$response = $e->getResponse();
$exception = $e->getResult()->exception;
$errorType = $e->getError();
$errorCode = $exception && $exception->getCode() ? $exception->getCode() : 500;
$errorMsg = $exception ? $exception->getMessage() : $errorType;
$json = new JsonModel(['message' => $errorMsg]);
$json->setTerminal(true);
$response->setStatusCode($errorCode);
$e->setResult($json);
$e->setViewModel($json);
}
}
That's all. Now on every error, your custom logics will be executed.

Adding new route to test client in Symfony

I am trying to add new route in test case extending Symfony\Bundle\FrameworkBundle\Test\KernelTestCase that will throw exception, to test EventListener for it. Simplified tests will looks like this:
public function testHandlingException()
{
$kernel = $this->createKernel(['environment' => 'test', 'debug' => true]);
$kernel->boot();
$client = $kernel->getContainer()->get('test.client');
$controller = new class extends Controller {
public function testAction()
{
throw new \Exception();
}
};
$route = new Route(
'route_500',
['_controller' => get_class($controller).'::testAction']
);
$client
->getContainer()
->get('router')
->getRouteCollection()
->add('route_500', $route);
$this->expectException(\Exception::class);
$client->request('GET', '/route_500');
}
It fails with:
Failed asserting that exception of type "Exception" is thrown.
How can I add route in-fly so calling it will work?
You can catch exceptions in unit tests. Not in end2end ones. If a GET request throw an exception you can get it: but you must catch that status code is 500 (for example). Another check could be that the response is containing the exception message.

form validation exception not catching by Exception in laravel 5.1?

In laravel5, I have catching all error at app/Exceptions/Handler#render function and it was working fine.
code given below,
public function render($request, Exception $e) {
$error_response['error'] = array(
'code' => NULL,
'message' => NULL,
'debug' => NULL
);
if ($e instanceof HttpException && $e->getStatusCode() == 422) {
$error_response['error']['code'] = 422;
$error_response['error']['message'] = $e->getMessage();
$error_response['error']['debug'] = null;
return new JsonResponse($error_response, 422);
}
}
return parent::render($request, $e);
}
But in laravel5.1,When form validation failes,it throws error message with 422exception. but it is not catching from app/Exceptions/Handler#render but working fine with abort(422).
How can I solve this?
You can catch simply by doing
public function render($request, Exception $e) {
if($e instanceof ValidationException) {
// Your code here
}
}
When Form Request fails to validate your data it fires the failedValidation(Validator $validator) method that throws HttpResponseException with a fresh Redirect Response, but not HttpException. This exception is caught via Laravel Router in its run(Request $request) method and that fetches the response and fires it. So you don't have any chance to handle it via your Exceptions Handler.
But if you want to change this behaviour you can overwrite failedValidation method in your Abstract Request or any other Request class and throw your own exception that you will handle in the Handler.
Or you can just overwrite response(array $errors) and create you own response that will be proceed by the Router automatically.

Categories