Akcium's avatar

Using custom exception for handling json responses

Hi there!

In some cases I have routes which should return some json response, since they are requested through AJAX. In this case I want to return some custom json data.

For example, if everything went well I return

   return ['success' => true]

from my controller.

However, in some cases something might have went wrong. I'm not talking about validation issues (they are handled perfectly by FormRequests).

If something went wrong I can throw an exception, for example in my Service class. However, if I change rendering of exception in global handler (which is located in Exceptions/Handler.php) I'll always get something like:

    ['success' => false, 'reason' => $e->getMessage()]

Even if the exception was thrown not by me, but it's just something general. Like "Trying to get property of non object".

Imagine this task. I have Order entity and it has status. I cannot change status from "new" to "finished", it's forbidden in my business logic. So in case user wants to change it, I should thrown an error which is understandable by frontend.

On the other hand if I got "trying to get a property of non object", I want to see full stacktrace. At least while I'm developing the app.

So I thought about making a special exception when I want to return formatted error. Something like "JsonResponseException", but this is wrong: an exception should mean something. Like "InvalidStatusChangeException" and in its render function I'll put

    ['success' => false, 'reason' => $e->getMessage()]

which would be fine.

But the thing is that way I'll need to create a lot of custom exception, which bothers me.

What would be the best approach here? Again: when it's a mistake in code I want to get full stacktrace, when I want to say something to frontend, I want to have a single handler. Plus, if possible, sending some error code/type would be cool.

Thanks in advance!

0 likes
5 replies
rawilk's avatar
rawilk
Best Answer
Level 47

What version of Laravel are you using? If it's 5.5, you can create custom Exception classes and return a certain type of response only with that exception. If you're using anything above 5.0 but less than 5.5, you can check for the instance in the Handler class inside of the render() function. I like to make an exception that I call GeneralException that I can throw during any Ajax requests that I make.

Before 5.5:


// This is coming from the Handler.php class

public function render($request, Exception $exception)
{

    if ($exception instanceof GeneralException) {
        return response()->json(['error' => true, 'message' => $exception->getMessage()]);
    }

    
    return parent::render($request, $exception);

}

Using 5.5:


<?php

namespace App\Exceptions;

use Exception;

/**
 * Class GeneralException
 */
class GeneralException extends Exception
{
    /**
     * Any extra data to send with the response.
     *
     * @var array
     */
    public $data = [];

    /**
     * The status code to use for the response.
     *
     * @var integer
     */
    public $status = 422;

    /**
     * Create a new exception instance.
     *
     * @param string $message
     */
    public function __construct($message)
    {
        parent::__construct($message);
    }

    /**
     * In Laravel 5.5, you can render your exceptions directly from the exception class
         * itself, allowing you to handle them they way you want to.
     */
    public function render($request)
    {
        if ($request->expectsJson()) {
            return $this->handleAjax();
        }

        return redirect()->back()
            ->withInput()
            ->withErrors($this->getMessage());
    }

    /**
     * Handle an ajax response.
     */
    private function handleAjax()
    {
        return response()->json([
            'error'   => true,
            'message' => $this->getMessage(),
            'data'    => $this->data
        ], $this->status);
    }

    /**
     * Set the extra data to send with the response.
     *
     * @param array $data
     *
     * @return $this
     */
    public function withData(array $data)
    {
        $this->data = $data;

        return $this;
    }

    /**
     * Set the HTTP status code to be used for the response.
     *
     * @param integer $status
     *
     * @return $this
     */
    public function withStatus($status)
    {
        $this->status = $status;

        return $this;
    }

}


If you really want exceptions specific for every error, by all means make them, but I feel using this approach works for most cases in the application.

2 likes
Akcium's avatar

I see. That's what I've been thinking, like making a GeneralException, just it felt a bit wrong for me but I think I'll stick to it.

Btw, I see that you have such things as $data. Could you show me an example of how do you throw this exception? Something like this:


$exception = new GeneralException("Message");
$exception->withData(...);

throw $exception;

?

rawilk's avatar

To send the data, you would throw the exception like this:


throw (new GeneralException('message'))->withData(['key' => 'value']);

2 likes
kamleshcgtechno's avatar

I am using Lumen 8 and my Handler.php code is as:

public function render($request, Throwable $exception) { if($exception->getStatusCode() == 404){ return response(view("errors.404"), 404); } if($exception->getStatusCode() == 500){ return response(view("errors.500"), 500); } return parent::render($request, $exception); }

I have added 404.blade.php and 500.blade.php files in resources/views/errors folder but it is not rendering to view file. Could you please suggest me what is the issue and how can we solve it. Look forward to hear any suggestion. Thanks a lot.

Please or to participate in this conversation.