Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Non relevant validations errors #36

Open
mohamed-abdul-fattah opened this issue Jun 5, 2017 · 7 comments
Open

Non relevant validations errors #36

mohamed-abdul-fattah opened this issue Jun 5, 2017 · 7 comments
Labels

Comments

@mohamed-abdul-fattah
Copy link

I've downloaded the package, and I'm trying to create an API with it. But when it comes to validations e.g. users registration, it throws this object
{ "message": "The given data failed to pass validation.", "status_code": 500 }

while the official lumen throws this object with relevant errors
{ "name": [ "The name field is required." ], "email": [ "The email field is required." ] }

And here is my validation code in UsersController.php at store method
$validation = $this->validate($request, [ 'name' => 'required', 'email' => 'required' ]);

So, what is missing here to throw this general message?

@krisanalfa
Copy link
Owner

Investigating this issue. I doubt it caused by Dingo Exception Handler.

@krisanalfa krisanalfa added the bug label Jun 6, 2017
@elbakly
Copy link

elbakly commented Jun 19, 2017

It is indeed the Dingo Exception Handler I have edited the handle function in the Dingo/Api/Exception/Handler it acts as temporary solution

`

public function handle(Exception $exception)
{

   foreach ($this->handlers as $hint => $handler) {

        if (! $exception instanceof $hint) {
            continue;
        }

        if ($response = $handler($exception)) {
            if (! $response instanceof BaseResponse) {
                $response = new Response($response, $this->getExceptionStatusCode($exception));
            }

            return $response;
        }
    }

    if($exception instanceof  ValidationException)
    {
            return $exception->getResponse();
    }
    return $this->genericResponse($exception);
}

`

@krisanalfa
Copy link
Owner

Wow, thanks @elbakly! I will make a patch for this. Should check for another exception.

@MaeseppTarvo
Copy link

@elbakly This is not fixing the problem. The output is still the same.

@mohamed-abdul-fattah
Copy link
Author

@MaeseppTarvo Here is the whole file, check the use part on the top of the file because I used a class to fix it besides @elbakly addition


namespace Dingo\Api\Exception;

use Exception;
use ReflectionFunction;
use Illuminate\Http\Response;
use Dingo\Api\Contract\Debug\ExceptionHandler;
use Illuminate\Validation\ValidationException;
use Dingo\Api\Contract\Debug\MessageBagErrors;
use Symfony\Component\HttpFoundation\Response as BaseResponse;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Illuminate\Contracts\Debug\ExceptionHandler as IlluminateExceptionHandler;

class Handler implements ExceptionHandler, IlluminateExceptionHandler
{
    /**
     * Array of exception handlers.
     *
     * @var array
     */
    protected $handlers = [];

    /**
     * Generic response format.
     *
     * @var array
     */
    protected $format;

    /**
     * Indicates if we are in debug mode.
     *
     * @var bool
     */
    protected $debug = false;

    /**
     * User defined replacements to merge with defaults.
     *
     * @var array
     */
    protected $replacements = [];

    /**
     * The parent Illuminate exception handler instance.
     *
     * @var \Illuminate\Contracts\Debug\ExceptionHandler
     */
    protected $parentHandler;

    /**
     * Create a new exception handler instance.
     *
     * @param \Illuminate\Contracts\Debug\ExceptionHandler $parentHandler
     * @param array                                        $format
     * @param bool                                         $debug
     *
     * @return void
     */
    public function __construct(IlluminateExceptionHandler $parentHandler, array $format, $debug)
    {
        $this->parentHandler = $parentHandler;
        $this->format = $format;
        $this->debug = $debug;
    }

    /**
     * Report or log an exception.
     *
     * @param \Exception $exception
     *
     * @return void
     */
    public function report(Exception $exception)
    {
        $this->parentHandler->report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     *
     * @param \Dingo\Api\Http\Request $request
     * @param \Exception              $exception
     *
     * @throws \Exception
     *
     * @return mixed
     */
    public function render($request, Exception $exception)
    {
        return $this->handle($exception);
    }

    /**
     * Render an exception to the console.
     *
     * @param \Symfony\Component\Console\Output\OutputInterface $output
     * @param \Exception                                        $exception
     *
     * @return mixed
     */
    public function renderForConsole($output, Exception $exception)
    {
        return $this->parentHandler->renderForConsole($output, $exception);
    }

    /**
     * Register a new exception handler.
     *
     * @param callable $callback
     *
     * @return void
     */
    public function register(callable $callback)
    {
        $hint = $this->handlerHint($callback);

        $this->handlers[$hint] = $callback;
    }

    /**
     * Handle an exception if it has an existing handler.
     *
     * @param \Exception $exception
     *
     * @return \Illuminate\Http\Response
     */
     public function handle(Exception $exception)
     {

        foreach ($this->handlers as $hint => $handler) {

             if (! $exception instanceof $hint) {
                 continue;
             }

             if ($response = $handler($exception)) {
                 if (! $response instanceof BaseResponse) {
                     $response = new Response($response, $this->getExceptionStatusCode($exception));
                 }

                 return $response;
             }
         }
         if ($exception instanceof ValidationException) {
             return $exception->getResponse();
         }

         return $this->genericResponse($exception);
     }

    /**
     * Handle a generic error response if there is no handler available.
     *
     * @param \Exception $exception
     *
     * @throws \Exception
     *
     * @return \Illuminate\Http\Response
     */
    protected function genericResponse(Exception $exception)
    {
        $replacements = $this->prepareReplacements($exception);

        $response = $this->newResponseArray();

        array_walk_recursive($response, function (&$value, $key) use ($exception, $replacements) {
            if (starts_with($value, ':') && isset($replacements[$value])) {
                $value = $replacements[$value];
            }
        });

        $response = $this->recursivelyRemoveEmptyReplacements($response);

        return new Response($response, $this->getStatusCode($exception), $this->getHeaders($exception));
    }

    /**
     * Get the status code from the exception.
     *
     * @param \Exception $exception
     *
     * @return int
     */
    protected function getStatusCode(Exception $exception)
    {
        return $exception instanceof HttpExceptionInterface ? $exception->getStatusCode() : 500;
    }

    /**
     * Get the headers from the exception.
     *
     * @param \Exception $exception
     *
     * @return array
     */
    protected function getHeaders(Exception $exception)
    {
        return $exception instanceof HttpExceptionInterface ? $exception->getHeaders() : [];
    }

    /**
     * Prepare the replacements array by gathering the keys and values.
     *
     * @param \Exception $exception
     *
     * @return array
     */
    protected function prepareReplacements(Exception $exception)
    {
        $statusCode = $this->getStatusCode($exception);

        if (! $message = $exception->getMessage()) {
            $message = sprintf('%d %s', $statusCode, Response::$statusTexts[$statusCode]);
        }

        $replacements = [
            ':message' => $message,
            ':status_code' => $statusCode,
        ];

        if ($exception instanceof MessageBagErrors && $exception->hasErrors()) {
            $replacements[':errors'] = $exception->getErrors();
        }

        if ($code = $exception->getCode()) {
            $replacements[':code'] = $code;
        }

        if ($this->runningInDebugMode()) {
            $replacements[':debug'] = [
                'line' => $exception->getLine(),
                'file' => $exception->getFile(),
                'class' => get_class($exception),
                'trace' => explode("\n", $exception->getTraceAsString()),
            ];
        }

        return array_merge($replacements, $this->replacements);
    }

    /**
     * Set user defined replacements.
     *
     * @param array $replacements
     *
     * @return void
     */
    public function setReplacements(array $replacements)
    {
        $this->replacements = $replacements;
    }

    /**
     * Recursirvely remove any empty replacement values in the response array.
     *
     * @param array $input
     *
     * @return array
     */
    protected function recursivelyRemoveEmptyReplacements(array $input)
    {
        foreach ($input as &$value) {
            if (is_array($value)) {
                $value = $this->recursivelyRemoveEmptyReplacements($value);
            }
        }

        return array_filter($input, function ($value) {
            if (is_string($value)) {
                return ! starts_with($value, ':');
            }

            return true;
        });
    }

    /**
     * Create a new response array with replacement values.
     *
     * @return array
     */
    protected function newResponseArray()
    {
        return $this->format;
    }

    /**
     * Get the exception status code.
     *
     * @param \Exception $exception
     * @param int        $defaultStatusCode
     *
     * @return int
     */
    protected function getExceptionStatusCode(Exception $exception, $defaultStatusCode = 500)
    {
        return ($exception instanceof HttpExceptionInterface) ? $exception->getStatusCode() : $defaultStatusCode;
    }

    /**
     * Determines if we are running in debug mode.
     *
     * @return bool
     */
    protected function runningInDebugMode()
    {
        return $this->debug;
    }

    /**
     * Get the hint for an exception handler.
     *
     * @param callable $callback
     *
     * @return string
     */
    protected function handlerHint(callable $callback)
    {
        $reflection = new ReflectionFunction($callback);

        $exception = $reflection->getParameters()[0];

        return $exception->getClass()->getName();
    }

    /**
     * Get the exception handlers.
     *
     * @return array
     */
    public function getHandlers()
    {
        return $this->handlers;
    }

    /**
     * Set the error format array.
     *
     * @param array $format
     *
     * @return void
     */
    public function setErrorFormat(array $format)
    {
        $this->format = $format;
    }

    /**
     * Set the debug mode.
     *
     * @param bool $debug
     *
     * @return void
     */
    public function setDebug($debug)
    {
        $this->debug = $debug;
    }
}

@MaeseppTarvo
Copy link

@elbakly Yayy. That was it, I was missing the use Illuminate\Validation\ValidationException;

@dav-is
Copy link

dav-is commented Aug 23, 2017

Is this still an issue?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants