25

I am new to Laravel and to Lumen. I want to ensure I am always getting only a JSON object as output. How can I do this in Lumen?

I can get a JSON response using response()->json($response);. But when an error happens, API giving me text/html errors. But I want only application/json responses.

Thanks in advance.

John Fonseka
  • 793
  • 1
  • 8
  • 15

4 Answers4

54

You'll need to adjust your exception handler (app/Exceptions/Handler.php) to return the response you want.

This is a very basic example of what can be done.

public function render($request, Exception $e)
{
    $rendered = parent::render($request, $e);

    return response()->json([
        'error' => [
            'code' => $rendered->getStatusCode(),
            'message' => $e->getMessage(),
        ]
    ], $rendered->getStatusCode());
}
Alexander Kim
  • 17,304
  • 23
  • 100
  • 157
Wader
  • 9,427
  • 1
  • 34
  • 38
  • Thanks for the answer. I have a little question however, Can I do this? `return response()->json(['code' => $rendered->getStatusCode(), 'message' => $e->getMessage()], $rendered->getStatusCode());` I searched for list of exceptions and couldn't find a list yet. – John Fonseka May 18 '16 at 10:50
  • That looks fine to me. Heres the signature for the `response()->json()` function. https://github.com/laravel/lumen-framework/blob/5.2/src/Http/ResponseFactory.php#L35 – Wader May 18 '16 at 11:02
  • 3
    Note that this example will always return a `200` HTTP code. You probably don't want that. If using `findOrFail()` for example, the `code` element will correctly show a `404`, but the overall result will still be a `200`, which it patently isn't. To fix this, pass `$rendered->getStatusCode()` into `json()` as its second parameter. – Jason Apr 29 '19 at 10:52
14

A more accurate solution based on @Wader's answer can be:

use Illuminate\Http\JsonResponse;

public function render($request, Exception $e)
{
    $parentRender = parent::render($request, $e);

    // if parent returns a JsonResponse 
    // for example in case of a ValidationException 
    if ($parentRender instanceof JsonResponse)
    {
        return $parentRender;
    }

    return new JsonResponse([
        'message' => $e instanceof HttpException
            ? $e->getMessage()
            : 'Server Error',
    ], $parentRender->status());
}
MTVS
  • 2,046
  • 5
  • 26
  • 37
8

Instead of touching the exception handler, I suggest you to add a middleware that sets the Accept header to application/json.

For example, you can create a middleware called RequestsAcceptJson and define it this way:

<?php

namespace App\Http\Middleware;

use Closure;

class RequestsAcceptJson
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $acceptHeader = strtolower($request->headers->get('accept'));

        // If the accept header is not set to application/json
        // We attach it and continue the request
        if ($acceptHeader !== 'application/json') {
            $request->headers->set('Accept', 'application/json');
        }

        return $next($request);
    }
}

Then you only need to register it as a global middleware to be run in every request to your api. In lumen you can do that by adding the class in the middleware call inside your bootstrap/app.php

$app->middleware([
    App\Http\Middleware\RequestsAcceptJson::class
]);

With Laravel it's pretty much the same process. Now the error handler will always return a json instead of plain text/html.

César Escudero
  • 320
  • 2
  • 12
0

I know this is quite an old question but I just stumbled across it. By default Lumen will return a JSON response if the requester "wants" it.

vendor/laravel/lumen-framework/src/Exceptions/Handler.php:110

return $request->expectsJson()
    ? $this->prepareJsonResponse($request, $e)
    : $this->prepareResponse($request, $e);

This goes down to vendor/illuminate/http/Concerns/InteractsWithContentTypes.php:52

$acceptable = $this->getAcceptableContentTypes();
return isset($acceptable[0]) && Str::contains($acceptable[0], ['/json', '+json']);

Which means if you specify an "Accept" header for with "application/json" lumen will automatically return a JSON response. e.g curl -H "Accept: application/json" https://example.com/my-erroring-endpint

Using this saves you from having to write a custom error handler.

Adam
  • 121
  • 4