Closed lighningwolf closed 1 year ago
Hey @lighningwolf
I copied the sample code from the documentation to Laravel's App\Exceptions\Handler.php
. When I trigger an error (e.g. abort(500)
) and inspect the response on the browser's network tab, I do get a proper Inertia response including the shared properties:
{
"component": "Error",
"props": {
"errors": {},
"auth": {
"user": {
"id": 1,
...
}
},
"test": "this is another shared property form App\\Http\\Middleware\\HandleInertiaRequests",
"status": 500
},
"url": "/profile",
"version": "a3dc1f89dc0f51e87d1a0c4e18c8e9f1"
}
I am using Laravel v8.69. So it seems to work out of the box. Maybe you can simply add a "test" property to the shared data, like I did? Or try with a fresh Laravel/Inertia install and narrow down the error there?
This is also more likely an issue for Inertia's Laravel adapter..
I had the same issue as @lighningwolf. My Error.vue page uses a template that has required shared data however that data was not passed to Vue. ie: HandleInertiaRequests
is not used. I had to pass the data manually like so:
public function render($request, Throwable $e)
{
$response = parent::render($request, $e);
if (!app()->environment(['local', 'testing']) && in_array($response->status(), [500, 503, 404, 403])) {
return Inertia::render('Error', ['status' => $response->status()])
->with([
'user' => fn () => $request->user()
? $request->user()->only('id', 'name', 'email')
: null,
])
->toResponse($request)
->setStatusCode($response->status());
} else if ($response->status() === 419) {
return back()->with([
'message' => 'The page expired, please try again.',
]);
}
return $response;
}
Hey @devinfd
can you simply add a string as "test" to your share
method, like i did above. Does it come through?
What error / status code is your issue about?
Please also inspect your Inertia response wihtin the Browser Dev tools, look for the prop. Please share your HandleInertiaRequests
middleware or maybe you share your repo to reproduce. Thank you!
My HandleInertiaRequests
:
class HandleInertiaRequests extends Middleware
{
/**
* The root template that's loaded on the first page visit.
*
* @see https://inertiajs.com/server-side-setup#root-template
* @var string
*/
protected $rootView = 'app';
/**
* Determines the current asset version.
*
* @see https://inertiajs.com/asset-versioning
* @param \Illuminate\Http\Request $request
* @return string|null
*/
public function version(Request $request)
{
return parent::version($request);
}
/**
* Defines the props that are shared by default.
*
* @see https://inertiajs.com/shared-data
* @param \Illuminate\Http\Request $request
* @return array
*/
public function share(Request $request)
{
return array_merge(parent::share($request), [
'appName' => config('app.name'),
'test' => 'foo',
'auth.user' => fn () => $request->user()
? $request->user()->only('id', 'name', 'email')
: null
]);
}
}
A "test" property in HandleInertiaRequests
is not shared with Error.vue
All I wanted to do was create a file/view to handle 404 errors.
Hey @devinfd
Thank you! The 404 status code was the missing piece. Laravel does not apply route middleware groups from the $middlewareGroups
-array inside Kernel.php
if the route is not hit. Only the global HTTP middleware stack from the $middleware
-array is applied.
You now think that could simply move Inertia's middelware to the global stack but this will not work as e.g. the Session store not set there and the middleware will throw an error.
It does work with all other errors, e.g. from your route-controllers though. So your solution with the manual props is possibly the easiest one.
Note that this is not a bug in Inertia but how Laravel works in general.
Hi, I'm really sorry for the lack of response I have to admit I totally forgot about this issue I've had to move on other subjects despite this one was not solved.
@ajnsn I get what you said about the middleware I have to admit it was logical yet I didn't thought about it. What I don't get is what you mean about the status code, my Exception/Handler is sending the code (same way as what @devinfd shows in his code) so I don't understand what is the real missing part to fetch my inertia data, do I really need to duplicate my share
method from HandleInertiaRequests
?
Because now I have:
Handler@render
/**
* Prepare exception for rendering.
*
* @param $request
* @param Throwable $e
* @return JsonResponse|Response|\Symfony\Component\HttpFoundation\Response
* @throws Throwable
*/
public function render($request, Throwable $e): JsonResponse|Response|\Symfony\Component\HttpFoundation\Response
{
$response = parent::render($request, $e);
$user = $request->user()
? User::where('id', $request->user()->id)
->with([
'roles' => fn($query) => $query->select('id', 'name')->with([
'permissions' => fn($query) => $query->select('name')
]),
])
->select('id', 'email', 'name')
->sole()
: null;
$companies = [];
if($user){
$companies = $user->hasRole('admin') ? Company::all() : Collaborator::find($user->id)->companies;
}
if (!app()->environment(['local', 'testing']) && in_array($response->status(), [500, 503, 404, 403])) {
return Inertia::render('Errors/Index', ['status' => $response->status()])
->with([
'flash' => [
'success' => session()->get('success'),
'error' => session()->get('error'),
],
'route' => Route::currentRouteName(),
'tenant' => tenant(),
'companies' => $companies ?? [],
'auth.user' => $user
])
->toResponse($request)
->setStatusCode($response->status());
}
if ($response->status() === 419) {
return back()->with([
'message' => 'The page expired, please try again.',
]);
}
return $response;
}
}
The with
has the exact same attributes as what my share
method returns in HandleInertiaRequests
yet when I go to a wrong URL (which leads to a 404 exception) I have just an object full of null values
{
auth:{
user:null
}
companies:Array[0]
flash:{
error:null
success:null
},
route:null
tenant:null
}
What am I missing here ?
Hey @lighningwolf
Laravel's request lifecylce is described here. Long story, short: Some middleware's, like the Session (\Illuminate\Session\Middleware\StartSession::class
) middleware, are only attached to middlware group web
, look at your App\Http\Kernel.php
. You also attach your HandleInertiaRequests
middleware to this group as described in the docs.
If you now hit a unknown route (404), Laravel will only process some basic middleware and does not process the web
middleware group. Therefore also some other things wont work, even if you pass them in your Exception handler, e.g.
session()
- as there is no Session middleware availableRoute::currentRouteName()
- as there is no route found.I cannot say anything about your tenant()
method but the same most likely applies.
Hope this helps!
@ajnsn thanks for your really quick answer, as you suggested I moved the HandleInertiaRequests::class
from the web
group to the $middleware
array (with the StartSession
) now if I go to an unknown route (i'm sticking with 404) I have nothing on screen but i i dd
before the return in the handler I see the dump on screen yet my return Inertia::render()->asResponse.....
does not render the requested error page, I might be abusing your time and I'm sorry but did you have any idea why ? English is not my primary language I might have missed something from what you said
Hey @lighningwolf
oh, I did not directly suggest to do that, sorry if that was unclear. Moving middleware from the web
middleware group to the global middleware stack could have side effects.
You propably would encounter all these things if you use just use Laravel Blade, without Inertia as these are basic Laravel things.
Anyway here's a quick find about these issue and possible solution - but haven't tried that and it's from 2017 - so test carefully.
Edit: Fallback routes could also be an easy solution for the 404 issue. Just return Inertia::render
inside the fallback-callback could work?
Try this solution.
namespace App\Exceptions;
use App\Http\Middleware\HandleInertiaRequests;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Symfony\Component\HttpKernel\Exception\HttpException;
class Handler extends \Illuminate\Foundation\Exceptions\Handler
{
/**
* Register the exception handling callbacks for the application.
*
* @return void
*/
public function register()
{
$this->renderable(function (HttpException $e, Request $request) {
$code = $e->getStatusCode();
$middleware = new HandleInertiaRequests;
$inertia = Inertia::getFacadeRoot();
$inertia->version($middleware->version($request));
foreach ($middleware->share($request) as $key => $value) {
$inertia->share($key, $value);
}
return $inertia->render('errors/' . $code)
->toResponse($request)
->setStatusCode($code);
});
}
}
Also add a fallback route to handle 404 error.
Route::fallback(fn () => inertia('errors/404'));
Just add a fallback route for 404 error and follow the Error Handling guide https://inertiajs.com/error-handling
Route::fallback(fn () => abort(404));
Hey! Thanks so much for your interest in Inertia.js and for sharing this issue/suggestion.
In an attempt to get on top of the issues and pull requests on this project I am going through all the older issues and PRs and closing them, as there's a decent chance that they have since been resolved or are simply not relevant any longer. My hope is that with a "clean slate" me and the other project maintainers will be able to better keep on top of issues and PRs moving forward.
Of course there's a chance that this issue is still relevant, and if that's the case feel free to simply submit a new issue. The only thing I ask is that you please include a super minimal reproduction of the issue as a Git repo. This makes it much easier for us to reproduce things on our end and ultimately fix it.
Really not trying to be dismissive here, I just need to find a way to get this project back into a state that I am able to maintain it. Hope that makes sense! ❤️
Versions:
Describe the problem:
When using custom error pages (i.e 404, 500 pages) Inertia doesn't send the SharedData to the client, and in the ExceptionHandler things like:
auth()->user()
,tenant()
(from Tenancy for Laravel arenull
while I expect user to be authenticated and on a specific tenant at the moment of the error.My error pages are using the Layout of the app this is why I need my exception handler to send me this data.
What I've done so far
Followed the inertia js documentation about custom errors. My environment is local so I removed the check for this specific environment during the development.
It works well until I add my Layout to the page in the Vue component which was designed to receive som mandatory shared data.