itsgoingd / clockwork

Clockwork - php dev tools in your browser - server-side component
https://underground.works/clockwork
MIT License
5.62k stars 323 forks source link

Store method doesn't work (returns 404) #677

Open zaminhub opened 7 months ago

zaminhub commented 7 months ago

Stack Laravel 10.43 + Jetstream v4.22 (inertia v0.6.11 + vue v3.2.31). php v8.1 After clockwork package installed the store methods of any resourse controller doesn't work and returns 404 page not found response. After removing clockwork package the problem disappears. I suppose this is because headers which should be "Accept: application/json", but I am not sure. Somehow clockwork package does remove this header from all post requests, but not from put/patch request.

itsgoingd commented 7 months ago

Hey, I can't see any weird behavior with a new Laravel + Jetstream installation. Can you share a simple repo demonstrating this issue?

zaminhub commented 5 months ago

Sorry for late response. Here is demo repo https://github.com/zaminhub/jetstream.test.git cloclwork installed store method doesn't work.

zaminhub commented 2 months ago

Hello @itsgoingd Did you check the demo repo above?

itsgoingd commented 2 months ago

Hey, sorry, forgot about this one, thanks for reminding me. Turns out this is quite a convoluted issue. I will try to explain.

The code that triggers this behavior is this line in your javascript code:

https://github.com/zaminhub/jetstream.test/blob/master/resources/js/Pages/Posts/PostModal.vue#L19

This ends up sending a POST request with an empty _method override when trying to create new posts. When editing existing posts, the override is set to PUT, making it work.

On server-side, Clockwork checks the method of the incoming request, very early in the processing. To do so, we call $request->getMethod(), which is a method from the underlying Symfony HTTP request implementation. This method does a bunch of things, resolves the base request method, tries to apply various types of overrides and validates the result.

Since your app is sending request with an empty _method override, the method executes all the way up to the following validation step, which fails (since $method is an empty string) and an exception is thrown. I assume since this is very early in the request processing, the exception ends up being interpreted as 404 error (haven't looked into this part that much).

https://github.com/symfony/http-foundation/blob/7.1/Request.php#L1166-L1168

Well, a good question is, why does this work without Clockwork?

Turns out, Laravel includes a middleware, that replaces empty strings in the request by nulls. Now when we call the same getMethod() method, we will hit this early return and never reach the exception part.

https://github.com/symfony/http-foundation/blob/7.1/Request.php#L1156-L1158

The issue with Clockwork installed is the execution order, which looks like this:

Request is received
Clockwork calls getMethod() // _method is empty string and exception is thrown
ConvertEmptyStringsToNull middleware is ran
Routing calls getMethod() // _method is null now, so the call defaults to POST

TL;DR I'm not sure if or how we want to fix this in Clockwork, but you can trivially fix this on your side, by adding POST as default value to _method, or not including that key at all.