laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.45k stars 11k forks source link

$request->replace Method not working in the Testing Environment #24547

Closed skmail closed 6 years ago

skmail commented 6 years ago

Description:

Using $request->replace() is not working as expected when calling an endpoint using json('method','uri') in the Unit/Integration tests.

When i use $this->replace(['name' => 'test']) in the form request and try to access the value in the controller using $request->get('name') it returns null, and it seems only happen when call an endpoint using the json() method while it works fine with other methods like get(),post(),put().

I put the following in the Form Request

    public function rules()
    {
        $this->replace([
            "name" => "Test",
        ]);

        return [
            //
        ];
    }

In the controller i did the following

    public function sanitize(SanitizeRequest $request)
    {
        return [
            'name' => $request->get('name')
        ];
    }

The $request->get('name') return null.

Steps To Reproduce:

I made a git repository with Test to replicate this issue

https://github.com/skmail/laravel-request-sanitize

Files to review:

Tests tests/Feature/RequestSanitizeTest.php

PS: $request->all(), returns the replaced items without any issues

Thanks

lagbox commented 6 years ago

You probably don't want to be calling Request::get almost ever btw (it is never used in the documentation for a reason), use Request::input ... Request::all uses Request::input ... Request::input is Laravel's method for retrieving inputs (uses the priorities that Laravel prefers, get is from symfony request)

lagbox commented 6 years ago

Using the correct method to pull from the correct input source, $request->input(), you shouldn't have an issue.

Though this does illustrates a difference between the FormRequest and a regular Request. When the regular Request is created the request property is set to the input source, $request->request = $request->getInputSource();. When sending a json request this will get set to the json parameter bag from $request->json(). $request->request === $request->json(). When calling $request->replace() it gets the input source, json, and calls replace on that parameter bag ... which happens to be the same bag as $request->request.

For the FormRequest this does not happen. It gets filled with arrays that end up in new parameter bags. When you call $request->replace() it is calling replace on the correct input source, json. If you were to get that parameter bag it will have the replacements. $request->request is not the same object as $request->json() so $request->request is still holding the original values or lack there of.

$request->get() (the symfony method) is checking $this->attributes, $this->query and $this->request to find the key. Illuminate\Http\Request::input is pulling the input source, json in this case, and the querystring looking for the key. It is the preferred way to get inputs from the request.

skmail commented 6 years ago

Thanks for the clarification

lagbox commented 6 years ago

No problem.

Now I am interested to figure out if that difference between FormRequest and Request in this respect was intentional or not. :-)

skmail commented 6 years ago

I think the difference is that FormRequest validates the user Inputs directly when the type hinted FormRequest class in the action resolved by the container, while the Request not validating any thing.

You may find this helpful.

https://github.com/laravel/framework/blob/b0c2459d7e55519d1c61927ab526e489a3a52eaf/src/Illuminate/Foundation/Providers/FormRequestServiceProvider.php

lagbox commented 6 years ago

@themsaid Would you happen to know why Illuminate\Http\Request@createFromBase has a $request->request = $request->getInputSource() but @createFrom does not do this tying of $request->request to the current input source?