Im trying intercept errors in laravel and i found a nice way to do that:
Simulating a error:
public function index(){
$users = User::all(); //<-SQL exeception here
return response()->json(['message'=>'ok'], 200);
}
app/Exceptions/Handler.php
public function report(Exception $exception)
{
dd($exception); //<-intercept my error here
parent::report($exception);
}
Works very well and i can do whatever i want with error, but when i use a try-catch block, my interceptor does not work:
Simulating error again
public function index(){
try {
$users = User::all();//<-SQL exeception here
} catch (\Throwable $th) {
error_log('Error handled');
//MyInterceptor::manuallyIntercept($th);
}
return response()->json(['message'=>'ok'], 200);
}
Is there a clean way to intercept all handled errors programatically?
Not report method, you need to use render method on Handler.php
You will see $this->errorResponse which is to just return JSON response. I just want to show the main idea.
public function render($request, Exception $exception)
{
if ($exception instanceof ValidationException) {
return $this->convertValidationExceptionToResponse($exception, $request);
}
if ($exception instanceof ModelNotFoundException) {
$modelName = strtolower(class_basename($exception->getModel()));
return $this->errorResponse("Does not exists any {$modelName} with the specified identificator", 404);
}
if ($exception instanceof AuthenticationException) {
return $this->unauthenticated($request, $exception);
}
if ($exception instanceof AuthorizationException) {
return $this->errorResponse($exception->getMessage(), 403);
}
if ($exception instanceof MethodNotAllowedHttpException) {
return $this->errorResponse('The specified method for the request is invalid', 405);
}
if ($exception instanceof NotFoundHttpException) {
return $this->errorResponse('The specified URL cannot be found', 404);
}
if ($exception instanceof HttpException) {
return $this->errorResponse($exception->getMessage(), $exception->getStatusCode());
}
if ($exception instanceof QueryException) {
$errorCode = $exception->errorInfo[1];
if ($errorCode == 1451) {
return $this->errorResponse('Cannot remove this resource permanently. It is related with any other resource', 409);
}
}
if (config('app.debug')) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected Exception. Try later', 500);
}
Error response method
protected function errorResponse($message, $code)
{
return response()->json(['error' => $message, 'code' => $code], $code);
}
Related
Laravel Framework 8.48.0
PHP 7.4.18
Local Environment: Xampp, Windows10
I am developing RESTful API using Laravel 8. I have written some logic inside the register() method to make the error response better and more straightforward. Everything is working perfectly. But the problem is when Laravel detects an exception that doesn't fall any of this logic and APP_DEBUG=true, this piece of code start executing and takes too long to respond; it says execution timeout. If I clean the register() method, it shows an exception within 1 or 2 secs. What's wrong with my code?
I want a better and more precise error response + when APP_DEBUG=true quickly shows an error exception.
I think this is the culprit.
if (env('APP_DEBUG', false)) {
return parent::render($request, $exception);
}
Handler.php
class Handler extends ExceptionHandler
{
use ApiResponser;
protected $dontReport = [
//
];
protected $dontFlash = [
'password',
'password_confirmation',
];
public function register()
{
$this->renderable(function (Exception $exception, $request) {
if ($request->wantsJson()) {
if ($exception instanceof HttpException) {
$code = $exception->getStatusCode();
$message = Response::$statusTexts[$code];
return $this->errorResponse($message, $code);
}
if ($exception instanceof AuthenticationException) {
return $this->errorResponse($exception->getMessage(), Response::HTTP_UNAUTHORIZED);
}
if ($exception instanceof AuthorizationException) {
dd('authorization exception');
return $this->errorResponse($exception->getMessage(), Response::HTTP_FORBIDDEN);
}
if ($exception instanceof ValidationException) {
$errors = $exception->validator->errors()->getMessages();
return $this->errorResponse($errors, Response::HTTP_UNPROCESSABLE_ENTITY);
}
if (env('APP_DEBUG', false)) {
return parent::render($request, $exception);
}
return $this->errorResponse('Unexpected error occurred. Please contact your administrator for help.', Response::HTTP_INTERNAL_SERVER_ERROR);
}
});
}
}
As we know, we can define every RestApi in Laravel and we can implementing them into Route or Controllers, this below code is one of my simple RestApi which i want to convert that to GraphQl
public function loginAccount(RequestLoginAccount $request): JsonResponse
{
$user = User::where('mobile_number', $request->input('mobile_number'))->first();
if (!$user) {
return response()->json(['response' => false]);
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);
}
after creating simple resolver i have this query:
command:
php artisan lighthouse:query loginAccount
loginAccount query:
type Query {
///
loginAccount(mobile_number:String!, verify_code: String!): User
}
loginAccount resolver:
class LoginAccount
{
public function __invoke($_, array $args)
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return null;
}
/*try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return response()->json(['response' => -1]);
}
return response()->json(['response' => true]);*/
}
}
now could you help me to know whats equivalent of this RestApi in GraphQl? or is any custom response to define them?
I found how can i convert it from RestApi to GraphQl and it's resolved
i should be have custom type in schema:
type LoginResponce{
response:Int
}
custom GraphQl resolve should be has:
class LoginAccount
{
public function __invoke($_, array $args): array
{
$user = User::where('mobile_number', $args['mobile_number'])->first();
if (!$user) {
return ['response' => -1];
}
try {
$user->notify(new LoginVerifyCode($user->mobile_number));
} catch (Exception $exception) {
return ['response' => -2];
}
return ['response' => 1];
}
}
and query:
type Query {
//
loginAccount(mobile_number:String!): LoginResponce
}
GraphQl query:
query {
loginAccount(mobile_number: "00000") {
response
}
}
guys. I am new to Laravel. Just installed 5.5 and try to catch the AuthenticationException in App\Exceptions\Handler like below
public function render($request, Exception $exception)
{
if ($exception instanceof AuthenticationException) {
//Do something
}
}
The problem is ($exception instanceof AuthenticationException) always return false.
dd($exception instanceof AuthenticationException) //return false.
When I dd($exception) I got
AuthenticationException{
#gurad...
....
.....
}
Then I try
get_class($exception) return \Illuminate\Auth\AuthenticationException
However,
dd($exception instanceof Exception) //return true.
Please help. Thanks.
You should make sure you use class from valid namespace:
public function render($request, Exception $exception)
{
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
//Do something
}
return parent::render($request, $exception);
}
You mentioned:
dd($exception instanceof Exception) //return true.
That's true. Each exception class that will extend Exception class will return true for this, that's why in your handler you should make sure you first verify specific classes and not exception class, for example if you used:
public function render($request, Exception $exception)
{
if ($exception instanceof Exception) {
//Do something 1
}
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
//Do something 2
}
return parent::render($request, $exception);
}
always //Do something 1 would be launched first.
I have an application, which for each method it does a field validation before proceeding with code execution.
public function authenticateSeller(Request $request)
{
$fields = $request->all();
$rules = config("validation-rules.loads.access");
$validator = Validator::make($fields, $rules);
if($validator->fails()) {
throw new CustomFieldValidation($validator);
}
}
I needed to pass the object to the CustomFieldValidation class to pass to the Laravel Handler class to handle the errors and return in the JSON form. How to do this?
class CustomFieldValidation extends \Exception
{
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
}
}
I was hoping to manipulate the message handler's rendering method.
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
Can someone help me?
To answer your question: just add a $validator attribute to your exception class.
class CustomFieldValidation extends \Exception
{
public $validator;
public function __construct($validator,$message= NULL, $code = NULL, Exception $previous = NULL)
{
parent::__construct($message, $code, $previous);
$this->validator = $validator;
}
}
public function render($request, Exception $exception)
{
if($exception instanceof CustomFieldValidation) {
foreach($exception->validator->errors()->all() as $message) {
$errors[] = [
'message' => $message,
];
}
return response()->json($errors, 400, ['Content-type' => 'application/json; charset=utf-8'], JSON_UNESCAPED_UNICODE);
}
return parent::render($request, $exception);
}
#Samsquanch is right, the use case example you're showing is simple and it'd be better to just return JSON from the controller. However if you're throwing the exception from some other class, this would make sense.
I am building an RESTful API in Laravel 5.2.
In my resource controllers I want to use implicit model binding to show resources. e.g.
public function show(User $users)
{
return $this->respond($this->userTransformer->transform($users));
}
When a request is made for a resource that doesn't exist Laravel automatically returns the NotFoundHttpException
NotFoundHttpException
I want to return my own custom response but how can I do that for a query that is done using route model binding?
Would something like this Dingo API response answer be able to be implemented?
Or will I stick with my old code which was something like this:
public function show($id)
{
$user = User::find($id);
if ( ! $user ) {
return $this->respondNotFound('User does not exist');
}
return $this->respond($this->userTransformer->transform($users));
}
So I could see if a resource (user) was not found and return an appropriate response.
See if you can catch ModelNotFound instead.
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
dd('model not found');
}
return parent::render($request, $e);
}
I think a good place would be in the Handler.php file under /app/Exceptions
public function render($request, Exception $e)
{
if ($e instanceof NotFoundHttpException) {
// return your custom response
}
return parent::render($request, $e);
}
In Laravel 7 and 8 you can do something like this.
In app/Exception/Handler.php class, add the render() method like below(if it doesn't exist).
Note that instead of type hinting Exception class you should use Throwable .
use Throwable;
public function render($request, Throwable $e)
{
if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
//For API (json)
if (request()->wantsJson()) {
return response()->json([
'message' => 'Record Not Found !!!'
], 404);
}
//Normal
return view('PATH TO YOUR ERROR PAGE');
}
return parent::render($request, $e);
}