inertiajs / inertia-laravel

The Laravel adapter for Inertia.js.
https://inertiajs.com
MIT License
2.08k stars 234 forks source link

InertiaHandleRequest middleware bypassed when HTTP error occurs #535

Closed ToguyC closed 1 year ago

ToguyC commented 1 year ago

Hi !

I am currently creating a project involving Inertia for the first time, and I came across a weir behavior. Indeed, I tried to create some custom HTTP error pages for 404, 501, etc., but I found out that my shared data setup in the InertiaHandleRequest middleware were not shared on error pages (see: https://inertiajs.com/error-handling for the backend implementation of error redirection).

The data that I wanted to have globally no matter the state of the app was containing:

public function share(Request $request): array
{     
      return array_merge(parent::share($request), [
          'appName' => config('app.name'),
          'locale' => app()->getLocale(),
          'availableLocales' => config('app.available_locales'),
          'flash' => [
              'message' => $request->session()->get('message'),
              'success' => $request->session()->get('success'),
              'error' => $request->session()->get('error'),
          ],
          'auth' => function () use ($request) {
              return [
                  'user' => $request->user() ? [
                      'id' => $request->user()->id,
                      'email' => $request->user()->email,
                  ] : null,
              ];
          },
      ]);
  }

Those were never passed to my custom error view.

The workaround I found was to create a custom Laravel middleware called SharedDataMiddleware that share the most important data like auth, appName and locale, and register this middleware as a global middleware for all request no matter what kind.

protected $middleware = [
    // \App\Http\Middleware\TrustHosts::class,
    \App\Http\Middleware\TrustProxies::class,
    \Illuminate\Http\Middleware\HandleCors::class,
    \App\Http\Middleware\PreventRequestsDuringMaintenance::class,
    \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    \App\Http\Middleware\TrimStrings::class,
    \Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
    \App\Http\Middleware\SharedDataMiddleware::class,
];
class SharedDataMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        Inertia::share("appName", config("app.name"));
        Inertia::share("availableLocales", config("app.available_locales"));
        Inertia::share("locale", app()->getLocale());
        Inertia::share("auth", function () use ($request) {
            return [
                'user' => $request->user() ? [
                    'id' => $request->user()->id,
                    'email' => $request->user()->email,
                ] : null,
            ];
        });

        return $next($request);
    }
}

Is this a proper way to bypass the problem I have or is it not the recommended way ?

Please let me know if something is shady so I can fix my mistakes and continue with development.

Cheers.

jessarcher commented 1 year ago

It all depends on whether the error occurs before or after any "share" middleware is executed.

Your solution seems fine, although you may want to increase the priority of your middleware.

I would be mindful of depending on a database query to succeed (retrieving the user) in your error-handling approach though.