wujt
wujt

Reputation: 1328

How to catch Exceptions within AfterMiddleware in Lumen?

Lumen 5.4.

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        try {
            return $next($request);
        } catch (IpValidationException $e) {
            return response()->json($e->getMessage(), 422);
        } catch (RemoteException $e) {
            return response()->json($e->getMessage(), 503);
        } catch (BaseException $e) {
            return response()->json($e->getMessage(), 400);
        } catch (\Exception $e) {
            return response()->json($e->getMessage(), $e->getCode());
        }
    }
}

After the exception is raised, the $next($request) goes to the following function in Laravel\Lumen\Routing:

/**
 * Get the initial slice to begin the stack call.
 *
 * @param  \Closure  $destination
 * @return \Closure
 */
protected function prepareDestination(BaseClosure $destination)
{
    return function ($passable) use ($destination) {
        try {
            return call_user_func($destination, $passable);
        } catch (Exception $e) {
            return $this->handleException($passable, $e);
        } catch (Throwable $e) {
            return $this->handleException($passable, new FatalThrowableError($e));
        }
    };
}

And it's catched there, so I my AfterMiddleware is useless. Any ideas how to circumvent it? I've found a solution and moved all the exceptions to render() in my Handler class, but it is much more convienient to use middleware.

Upvotes: 0

Views: 3170

Answers (2)

Kiko Seijo
Kiko Seijo

Reputation: 701

This is how I handle mine, its working on Lumen 5.5.* and php 7.1.

Late answer, but hopefully it will helps someone else. 😀

<?php

namespace App\Exceptions;

use Exception;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Response;
use Illuminate\Validation\ValidationException;
use Laravel\Lumen\Exceptions\Handler as ExceptionHandler;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Illuminate\Http\Exception\HttpResponseException;
use Symfony\Component\Debug\Exception\FatalThrowableError;


class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that should not be reported.
     *
     * @var array
     */
    protected $dontReport = [
        AuthorizationException::class,
        HttpException::class,
        ModelNotFoundException::class,
        ValidationException::class,
    ];

    /**
     * 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)
    {
        parent::report($e);
    }

    /**
     * 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 (env('APP_DEBUG')) {
        //     return parent::render($request, $e);
        // }
        $response = [
            'success' => false
        ];
        $response['status']   = null;

        if ($e instanceof HttpResponseException) {
            $response['status']   = Response::HTTP_INTERNAL_SERVER_ERROR;
        } elseif ($e instanceof MethodNotAllowedHttpException) {
            $response['status'] = Response::HTTP_METHOD_NOT_ALLOWED;
        } elseif ($e instanceof NotFoundHttpException) {
            $response['status'] = Response::HTTP_NOT_FOUND;
        } elseif ($e instanceof AuthorizationException) {
            $response['status'] = Response::HTTP_FORBIDDEN;
            $e      = new AuthorizationException('HTTP_FORBIDDEN', $response['status']);
        } elseif ($e instanceof ValidationException && $e->getResponse()) {
            $response['status']   = Response::HTTP_BAD_REQUEST;
            $e        = new ValidationException('HTTP_BAD_REQUEST', $response['status'], $e);
        } elseif ($e instanceof ValidationException) {
            $response['status']   = 422;
            $response['errors'] = $e->validator->errors();
        } elseif ($e instanceof ModelNotFoundException) {
          $response['status'] = 404;
        } elseif ($e instanceof UnableToExecuteRequestException) {
            $response['status'] = $e->getCode();
        } elseif ($e instanceof FatalThrowableError) {
            $response['status'] = Response::HTTP_INTERNAL_SERVER_ERROR;
        } elseif ($e) {
            $response['status'] = Response::HTTP_INTERNAL_SERVER_ERROR;
            $response['unhandled'] = 'exception-lumen-ksoft';
        }

        if ($response['status']) {
            $response['message'] = $e->getMessage();
            $response['error_code'] = $e->getCode() ?? '';
            // $response['exception'] = $e->getTraceAsString() ?? '';
            if (app()->environment() == 'local'){
                $response['file'] = $e->getFile() ?? '';
                $response['line'] = $e->getLine() ?? '';
            }
            return response()->json($response, $response['status']);
        } else {
            return parent::render($request, $e);
        }

    }
}

Upvotes: 0

ayip
ayip

Reputation: 2543

I haven't tried this, but from Lumen's code, I think it is possible.

The handleException function invoked from prepareDestination checks if the ExceptionHanlder is bound to the container or not. If it is not, it is throwing the exception.

protected function handleException($passable, Exception $e)
    {
        if (! $this->container->bound(ExceptionHandler::class) || ! $passable instanceof Request) {
            throw $e;
        }

        $handler = $this->container->make(ExceptionHandler::class);

        $handler->report($e);

        return $handler->render($passable, $e);
    }

So, try by removing the below ExceptionHandler binding from bootstrap/app.php

$app->singleton(
    Illuminate\Contracts\Debug\ExceptionHandler::class,
    App\Exceptions\Handler::class
);

Upvotes: 1

Related Questions