12

I want to encode the JSON responses of my API to UTF-8, but every time I make a response I don't want to do this:

return response()->json($res,200,['Content-type'=>'application/json;charset=utf-8'],JSON_UNESCAPED_UNICODE);

So I thought about making a middleware for all API routes which handle(...) function would be this:

public function handle($request, Closure $next) {
    $response = $next($request);
    $response->header('Content-type','application/json; charset=utf-8');
    return $next($request);
}

The problem is that it doesn't work, the Content-type header of my responses is still application/json and not application/json; charset=utf-8; maybe because the json(...) function already sets a Content-type header and I cannot override it.

How should I do?

Thank you for your help.

JacopoStanchi
  • 1,962
  • 5
  • 33
  • 61

2 Answers2

32

Its right there in the documentation, you want to use after middleware (following code is from top of my head and it should work):

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {

        /** @var array $data */ // you need to return array from controller
        $data = $next($request);

        return response()->json($data, 200, ['Content-Type' => 'application/json;charset=UTF-8', 'Charset' => 'utf-8'],
        JSON_UNESCAPED_UNICODE);
    }
}

With above approach we can spot two anti-patterns:

  • First, crafting response in middleware (you should do it in controller).

  • Also, maybe using un-escaped JSON response should be avoided, because Laravel creators made default the "escaping to plain-text" for a reason, but if all your Web-API's Client-Apps support UTF-8, then un-escaped improves performance.

    Experience shows that most clients beside supporting it, they even assume UTF-8 if charset is missing from Content-Type header.

Removing middleware and using controller only

Put following code in app/Http/Controller.php

protected function jsonResponse($data, $code = 200)
{
    return response()->json($data, $code,
        ['Content-Type' => 'application/json;charset=UTF-8', 'Charset' => 'utf-8'], JSON_UNESCAPED_UNICODE);
}

in any of the controllers that are extended by base controller (app/Http/Controller.php) you can use $this->jsonResponse($data);

How pros do it

They use eloquent resources or if there is more going on fractal is the way to go (in Laravel use spatie wrapper - https://github.com/spatie/laravel-fractal).

Top-Master
  • 7,611
  • 5
  • 39
  • 71
Kyslik
  • 8,217
  • 5
  • 54
  • 87
0

In your JsonResource class just add:

/**
 * Customize the outgoing response for the resource.
 *
 * @param  \Illuminate\Http\Request
 * @param  \Illuminate\Http\Response
 * @return void
 */
public function withResponse($request, $response)
{
    $charset = $request->header('Accept-Charset', 'utf-8');
    if ($charset === 'utf-8') {
        $response->header('Charset', 'utf-8');
        $response->header('Content-Type','application/json; charset=utf-8');
        $response->setEncodingOptions(JSON_UNESCAPED_UNICODE);
    }
    // Alternative: iso-8859-1 which will be encoded by default using json escaping.
}

I want to note that when using single resources and collections together you must implement this for both: JsonResource AND ResourceCollection. Using something like EventResource::collection($events->get()) does NOT work.

Source: Github

NicoHood
  • 687
  • 5
  • 12