skip to Main Content

As title ask, I’m working a SPA backend api project, but since Larave 10 removed MaintenanceModeException, I wonder what’s the proper way to handle maintenance mode in Laravel 10?

My old way handle the MaintenanceModeException is like:

/**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->renderable(function (Throwable $e, $request) {
            if($request->is('api/*')) {
                switch(true)
                {
                    case $e instanceof MaintenanceModeException:
                        return (new SystemMaintenanceResponse($e))->response()->setStatusCode(503);
                }
            }
        });
    }

And now, Laravel 10 throw HttpException when in the maintenance mode, but HttpException contains not just the maintenance exception, there is no document about why remove the MaintenanceModeException and no new document about how to handle this in Laravel 10.

I need a specific response to tell the front end that I’m in maintenance mode, and I don’t know how to tell exactly if I’m in maintenance mode right now because of Laravel removed the MaintenanceModeException.

What could I handle the maintenance mode correctly in Laravel 10?

2

Answers


  1. Chosen as BEST ANSWER

    After few days research, I end up with the solution below, it allowed me use the maintenance exception as before.

    1. Create a custom exception

    Create a custom exception by php artisan make:exception MaintenanceModeException.

    <?php
    
    namespace AppExceptions;
    
    use Exception;
    
    class MaintenanceModeException extends Exception
    {
        protected $code = 50000;
        protected $message = "System maintenance.";
    }
    
    1. Edit the default middleware where Laravel handle the maintenance mode, and overwrite the handle function.

    Middleware path will be appHttpMiddlewarePreventRequestsDuringMaintenance.

    use AppExceptionsMaintenanceModeException;
    
    class PreventRequestsDuringMaintenance extends Middleware
    {
        /**
         * The URIs that should be reachable while maintenance mode is enabled.
         *
         * @var array<int, string>
         */
        protected $except = [
            //
        ];
    
        /**
         * Handle an incoming request.
         *
         * @param  IlluminateHttpRequest  $request
         * @param  Closure  $next
         * @return mixed
         *
         * @throws SymfonyComponentHttpKernelExceptionHttpException
         */
        public function handle($request, Closure $next)
        {
            if ($this->app->maintenanceMode()->active()) {
                $data = $this->app->maintenanceMode()->data();
    
                if (isset($data['secret']) && $request->path() === $data['secret']) {
                    return $this->bypassResponse($data['secret']);
                }
    
                if ($this->hasValidBypassCookie($request, $data) ||
                    $this->inExceptArray($request)) {
                    return $next($request);
                }
    
                if (isset($data['redirect'])) {
                    $path = $data['redirect'] === '/'
                                ? $data['redirect']
                                : trim($data['redirect'], '/');
    
                    if ($request->path() !== $path) {
                        return redirect($path);
                    }
                }
    
                if (isset($data['template'])) {
                    return response(
                        $data['template'],
                        $data['status'] ?? 503,
                        $this->getHeaders($data)
                    );
                }
    
                /* Origin will seems like this.
                throw new HttpException(
                    $data['status'] ?? 503,
                    'Service Unavailable',
                    null,
                    $this->getHeaders($data)
                );
                */
    
                // Overwrite with this part, force throw an exception when api routes.
                if($request->is('api/*') || $request->expectsJson())
                    throw new MaintenanceModeException();
                else
                    throw new HttpException(
                        $data['status'] ?? 503,
                        'Service Unavailable',
                        null,
                        $this->getHeaders($data)
                    );
            }
    
            return $next($request);
        }
    }
    

    After it done, I can handle the system maintenance just I did before.

    $this->renderable(function (Throwable $e, $request) {
        if ($request->is('api/*')) {
            switch (true) {
                case $e instanceof ValidationException:
                    return (new ValidationFailedResponse($e))->response()->setStatusCode(400);
                case $e instanceof AuthenticationException:
                    return (new AuthenticationResponse($e))->response()->setStatusCode(401);
                case $e instanceof AuthorizationException:
                case $e instanceof UnauthorizedException:
                    return (new AuthorizationResponse($e))->response()->setStatusCode(403);
                case $e instanceof NotFoundHttpException:
                case $e instanceof ItemNotFoundException:
                case $e instanceof ModelNotFoundException:
                    return (new NotFoundResponse($e))->response()->setStatusCode(404);
                case $e instanceof MethodNotAllowedHttpException:
                    return (new MethodNotAllowResponse($e))->response()->setStatusCode(405);
                case $e instanceof ThrottleRequestsException:
                    return (new ThrottleLimitResponse($e))->response()->setStatusCode(503);
                case $e instanceof MaintenanceModeException:
                    return (new ExceptionResponse($e))->response()->setStatusCode(503);
            }
    
            /* ----- Unknown errors ----- */
            return (new ExceptionResponse($e))->response()->setStatusCode(500);
        }
    });
    

    The way can't handle system maintenance by HttpException is because of the ThrottleRequestsException also extend the HttpException, the condition $e instanceof HttpException will return true on it and also the http status code is also 503 on this exception.

    As I known, there's more situation like these, for the api project, it seems must have to custom in Laravel 10, the default maintenance mode feature only design for web.


  2. In Laravel 10, the MaintenanceModeException has been removed, and instead, Laravel throws an HttpException with a status code of 503 when the application is in maintenance mode.

    To avoid the issue of handling HttpException with a 503 status code, which may include other types of exceptions like TooManyRequestsHttpException, you can check if the application is in maintenance mode using the IlluminateFoundationApplication instance.

    Here’s an example of how you can modify the exception handler to check for the maintenance mode using the application instance:

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->renderable(function (Throwable $e, $request) {
            if ($request->is('api/*')) {
                $app = $this->container->make(Application::class);
                if ($app->isDownForMaintenance()) {
                    return response()->json(['message' => 'The application is currently in maintenance mode. Please try again later.'], 503);
                }
                if ($e instanceof IlluminateFoundationHttpExceptionsMaintenanceModeException) {
                    return (new SystemMaintenanceResponse($e))->response()->setStatusCode(503);
                }
            }
        });
    }
    

    We’re first checking if the application is in maintenance mode using the isDownForMaintenance method of the IlluminateFoundationApplication instance. If the application is in maintenance mode, we can return a JSON response with a specific message and a 503 status code.

    If the application is not in maintenance mode and the thrown exception is an instance of MaintenanceModeException, we can return a custom response using your SystemMaintenanceResponse class, as in your original code.

    This approach ensures that the correct response is returned when the application is in maintenance mode, regardless of the type of exception that was thrown.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search