Laravel custom exception - php

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

Related

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.

Slim3/DRY - How to handle errors/exceptions correctly without duplicating code?

I'm working on a fairly large JSON API using Slim3. My controllers/actions are currently littered with the following:
return $response->withJson([
'status' => 'error',
'data' => null,
'message' => 'Username or password was incorrect'
]);
At certain points in the application anything can go wrong and the response needs to be appropriate. But one thing that is common is the error responses are always the same. The status is always error, the data is optional (in the case of form validation errors data will contain those) and message is set to indicate to the user or consumer of the API what went wrong.
I smell code duplication. How can I reduce the code duplication?
From the top of my head all I could think of doing was creating a custom exception, something like App\Exceptions\AppException that takes option data and the message will be obtained form $e->getMessage().
<?php
namespace App\Exceptions;
class AppException extends Exception
{
private $data;
public function __construct($message, $data = null, $code = 0, $previous = null)
{
$this->data = $data;
parent::__construct($message, $code, $previous);
}
public function getData()
{
return $this->data;
}
}
Following that create middleware that calls $next wrapped in a try/catch:
$app->add(function($request, $response, $next) {
try {
return $next($request, $response);
}
catch(\App\Exceptions\AppException $e)
{
$container->Logger->addCritical('Application Error: ' . $e->getMessage());
return $response->withJson([
'status' => 'error',
'data' => $e->getData(),
'message' => $e->getMessage()
]);
}
catch(\Exception $e)
{
$container->Logger->addCritical('Unhandled Exception: ' . $e->getMessage());
$container->SMSService->send(getenv('ADMIN_MOBILE'), "Shit has hit the fan! Run to your computer and check the error logs. Beep. Boop.");
return $response->withJson([
'status' => 'error',
'data' => null,
'message' => 'It is not possible to perform this action right now'
]);
}
});
Now all I have to do at points in the code is to throw new \App\Exceptions\AppException("Username or password incorrect", null).
My only issue with this is it feels like I'm using exceptions for the wrong reasons and it may make debugging a little more difficult.
Any suggestions on reducing the duplicates and cleaning up error responses?
You can achieve similar similar results by creating an error handler which outputs JSON.
namespace Slim\Handlers;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Message\ResponseInterface as Response;
final class ApiError extends \Slim\Handlers\Error
{
public function __invoke(Request $request, Response $response, \Exception $exception)
{
$status = $exception->getCode() ?: 500;
$data = [
"status" => "error",
"message" => $exception->getMessage(),
];
$body = json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);
return $response
->withStatus($status)
->withHeader("Content-type", "application/json")
->write($body);
}
}
You must also configure Slim to use your custom error handler.
$container = $app->getContainer();
$container["errorHandler"] = function ($container) {
return new Slim\Handlers\ApiError;
};
Check Slim API Skeleton for example implemention.

Symfony: How to return a JSON response from a Before Filter (Listener)?

From following this example I have managed to set up the below Listener/Before Filter to parse all requests to my API endpoint (ie. /api/v1) before any controller actions are processed. This is used to validate the request type and to throw an error if certain conditions are not met.
I would like to differentiate the error response based on the applications environment state. If we are in development or testing, I simply want to throw the error encountered. Alternatively, if in production mode I want to return the error as a JSON response. Is this possible? If not, how could I go about something similar?
I'm using Symfony v3.1.*, if that makes any difference.
namespace AppBundle\EventListener;
use AppBundle\Interfaces\ApiAuthenticatedController;
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class ApiBeforeListener
{
/**
* #var \Symfony\Component\DependencyInjection\ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* Validates a request for API resources before serving content
*
* #param \Symfony\Component\HttpKernel\Event\FilterControllerEvent $event
* #return mixed
*/
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller))
return;
if ($controller[0] instanceof ApiAuthenticatedController) {
$headers = $event->getRequest()->headers->all();
// only accept ajax requests
if(!isset($headers['x-requested-with'][0]) || strtolower($headers['x-requested-with'][0]) != 'xmlhttprequest') {
$error = new AccessDeniedHttpException('Unsupported request type.');
if (in_array($this->container->getParameter("kernel.environment"), ['dev', 'test'], true)) {
throw $error;
} else {
// return json response here for production environment
//
// For example:
//
// header('Content-Type: application/json');
//
// return json_encode([
// 'code' => $error->getCode(),
// 'status' => 'error',
// 'message' => $error->getMessage()
// ]);
}
}
}
}
}
Unlike most events, the FilterControllerEvent does not allow you to return a response object. Be nice if it did but oh well.
You have two basic choices.
The best one is to simply throw an exception and then add an exception listener. The exception listener can then return a JsonResponse based on the environment.
Another possibility to to create a controller which only returns your JsonResponse then use $event->setController($jsonErrorController) to point to it.
But I think throwing an exception is probably your best bet.
More details here: http://symfony.com/doc/current/reference/events.html

Laravel 5 : How to use findOrFail() method?

I just follow some tutorial and so far what I do is :
my App/Exceptions/Handler.php
<?php
...
use Illuminate\Database\Eloquent\ModelNotFoundException;
...
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException){
abort(404);
}
return parent::render($request, $e);
}
and my UsersController looks like this :
...
public function edit($id)
{
$data = User::findOrFail($id);
$roles = Role::where('title', '!=', 'Super Admin')->get();
return View('admin.user.edit', compact(['data', 'roles']));
}
...
with the above code if I visit http://my.url/users/10/edit I get NotFoundHttpException in Application.php line 901:, yes because there is no id 10 in my record, but with User::find($id); I get normal view without data, since no id 10 in my record.
What I want is show default 404 then redirect to somewhere or return something if record not found with User::findOrFail($id); ? How I can do that ?
Thanks, any help appreciated.
ps: .env APP_DEBUG = true
This does what you asked. No need for exceptions.
public function edit($id)
{
$data = User::find($id);
if ($data == null) {
// User not found, show 404 or whatever you want to do
// example:
return View('admin.user.notFound', [], 404);
} else {
$roles = Role::where('title', '!=', 'Super Admin')->get();
return View('admin.user.edit', compact(['data', 'roles']));
}
}
Your exception handler is not necessary as it is. Regarding Illuminate\Database\Eloquent\ModelNotFoundException:
If the exception is not caught, a 404 HTTP response is automatically sent back to the user, so it is not necessary to write explicit checks to return 404 responses when using [findOrFail()].
Also, I'm pretty sure you get the exception page instead of 404 now because you're in debug mode.
public function singleUser($id)
{
try {
$user= User::FindOrFail($id);
return response()->json(['user'=>user], 200);
} catch (\Exception $e) {
return response()->json(['message'=>'user not found!'], 404);
}
}
findOrFail() is alike of find() function with one extra ability - to throws the Not Found Exceptions
Sometimes you may wish to throw an exception if a model is not found. This is particularly useful in routes or controllers. The findOrFail and firstOrFail methods will retrieve the first result of the query; however, if no result is found, a Illuminate\Database\Eloquent\ModelNotFoundException will be thrown:
$model = App\Flight::findOrFail(1);
$model = App\Flight::where('legs', '>', 100)->firstOrFail();
If the exception is not caught, a 404 HTTP response is automatically sent back to the user. It is not necessary to write explicit checks to return 404 responses when using these methods:
Route::get('/api/flights/{id}', function ($id) {
return App\Flight::findOrFail($id);
});
Its not recommended but If still you want to handle this exception globally, following are the changes as per your handle.php
/**
* 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 \Illuminate\Database\Eloquent\ModelNotFoundException) {
//redirect to errors.custom view page
return response()->view('errors.custom', [], 404);
}
return parent::render($request, $exception);
}
Late addition to above topic: If you want to handle the exception for an API backend and you don't want to make the check for an empty result in each method and return a 400 Bad request error individually like this...
public function open($ingredient_id){
$ingredient = Ingredient::find($ingredient_id);
if(!$ingredient){
return response()->json(['error' => 1, 'message' => 'Unable to find Ingredient with ID '. $ingredient_id], 400);
}
return $ingredient;
}
Instead use findOrFailand catch exception in app/Exceptions/Handler.php.
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
return response()->json(['error'=>1,'message'=> 'ModelNotFoundException handled for API' ], 400);
}
return parent::render($request, $exception);
}
This will then look like this in your Controllers:
public function open($ingredient_id){
return Ingredient::findOrFail($ingredient_id);
}
which is much cleaner. Consider that you have plenty of Models and plenty of Controllers.

How can I customize the validation error message in the Symfony's Controller

I have an ajax call to an url corresponding to a symfony2 coded page and in that page I have validation of one data passed to the page.
My validator :
/**
* #Assert\Regex(pattern="/^(https?:\/\/)/", message="wrong_string", groups={"searchnow_url_post"})
...
*/
public $searchstring;
When I build the answer to the ajax call I write it directly in the controller using JsonResponse. My problem is when validation produces an error I do something like:
return new JsonResponse(array(
'status' => 401,
'error_msg'=> (string)$errors
));
To be more precise, the whole controller is :
class BaseController extends Controller
{
public function searchnow_url_postAction(){
$input_string=$_REQUEST['src'];
$search_string= new Searchstring();
$search_string->setSearchstring($input_string);
//qui devo usare il validation group "search_now"
$validator = $this->get('validator');
$errors = $validator->validate($search_string, null, array('searchnow_url_post'));
if (count($errors) > 0){
$status=401;
return new JsonResponse(
array('status' => 401,
'error_msg'=> (string) $errors
));
}
else
return new JsonResponse(array('status' => 200));
}
}
(The above validator is extracted from the entity Searchstring)
In the javascript I receive in error_msg :
"Object(MemoBundle\Entity\Searchstring).searchstring: Wrong string."
but this is not good.
I would like to receive in error_msg only the message "Wrong string" without "Object(MemoBundle\Entity\Searchstring).searchstring:".
How can I do to receive this?
You haven't shown any of the code responsible for generating the output itself. As you can see, your error message showed up as expected
Somewhere you are calling your form object,
$form->getErrors( ... )
This is the relevant block.
I've got the solution. The issue was very simple but being new to Symfony2 I had some difficulties to focalize my problem.
I need to use the getMessage() method of ConstraintViolation object , where a list of this objects is returned from validate method of validator object.
To write some code lines the controller above now sounds like this:
public function searchnow_url_postAction(){
$input_string=$_REQUEST['src'];
$search_string= new Searchstring();
$search_string->setSearchstring($input_string);
//qui devo usare il validation group "search_now"
$validator = $this->get('validator');
$errors = $validator->validate($search_string, null, array('searchnow_url_post'));
if (count($errors) > 0){
$error_ret_msg='';
foreach ($errors as $error){
$error_ret_msg.=$error->getMessage()."|";
}
$status=401;
return new JsonResponse(
array('status' => 401,
'error_msg'=> $error_ret_msg,
));
}
else
return new JsonResponse(array('status' => 200));
}
I've used a foreach cycle in the case there are more validation constraints violated for the same variable, separating each error message with a "|".

Categories