livewire / livewire

A full-stack framework for Laravel that takes the pain out of building dynamic UIs.
MIT License
22.36k stars 1.56k forks source link

CorruptComponentPayloadException #254

Closed oliverbj closed 5 years ago

oliverbj commented 5 years ago

Describe the bug I have a blade view called show.blade.php, that includes a Livewire component:

 @livewire('streams.editor.document-viewer', $stream, $field)

When I load the view, it works fine. On my Livewire view, I have a method like below:

<!-- View component -->
<a wire:click="toggleNextStep">Confirm Selection</a>

 <!-- Component --> 
public $showDocumentScreen = true;

public function toggleNextStep()
{
     $this->showDocumentScreen = false;
}

In my document-viewer.blade.php view component, I have this defined:

@if($showDocumentScreen)
<!-- bla bla bla -->
@else
    <div>
        @livewire('streams.editor.another-view')
    </div>
@endif

When calling the toggleNextStep methods, it simply set's the $showDocumentScreen to false - and so it should show the another-view view component instead.

However, after I updated to the newest Livewire version (0.3.4), I get below error:

Livewire encountered corrupt data when trying to hydrate the [streams.editor.document-viewer] component. Ensure that the [name, id, data] of the Livewire component wasn't tampered with between requests.

Expected behavior I am trying to dynamically and conditionally show Livewire views (toggle next view if condition is true/false for example).

Additional context I am using Laravel 5.8.

FabioDePaoli commented 5 years ago

I have the same issue. When I try to set the value of a bind variable by a input field wire:model="body" through a on click event wire:click="send" it fails with this error: Livewire encountered corrupt data when trying to hydrate the [comments] component. Ensure that the [name, id, data] of the Livewire component wasn't tampered with between requests.

In the PHP function I only do this: public function send() { $this->body = ''; }

oliverbj commented 5 years ago

I just saw that I actually get this error message, no matter what method I use on the page. I also have a method that simply updates a string:

<a wire:click="nextPage">Next Page</a>

Which just updates below:

public function nextPage()
{
     $this->currentPage++;
}

When firing of this method, I get the same error message.

oliverbj commented 5 years ago

So I just checked the ComponentChecksumManager.php file that is handling the checking of the checksum.

If I remove the $id from the generate() method, it seems not to fail.

 public function generate($name, $data)
    {
        $hashKey = app('encrypter')->getKey();

        $stringForHashing = $name . json_encode($data);

        return hash_hmac('sha256', $stringForHashing, $hashKey);
    }

I am a bit unsure if this actually makes the whole check pointless - however, it seems to generate a different id at some point?

oliverbj commented 5 years ago

So a bit more context on this issue. I am trying to log the id and name of the components like this:

Log::debug($name . ':' . $id);

When I load the original page, with my component (document-viewer), I get these components & ids:

(On this page, I have a modal and my main component)

components.modal:TZ9MW0KF5ScY4IyhYUmM 
streams.editor.document-viewer:tMBFtlU81CcnbJ8d5Tmu

When I click on the method nextPage, this is the result:

streams.editor.document-viewer:tMBFtlU81CcnbJ8d5Tmu

So the ID is the same it seems.

This is a bit odd, since it worked yesterday if I removed the $id from the check.

Digging deeper, I tried to log the entire $stringForHashing and compare them with an online "string comparing tool".

On page load:

[...] "modalView":"","modalId":""}

When clicking on a method:

[...] "modalView":null,"modalId":null}

It seems like, that in my Modal.php component, I have this:

    public $modalView = '';
    public $modalId = '';

Which initially sets the modal view and id to an empty string. Changing these to:

    public $modalView = null;
    public $modalId = null;

Fixed the issue for me. Now the data payload is the same.

So basically, to fix this issue:

Go into ComponentChecksumManager.php and add a log method to compare the $stringForHashing for each request:

    public function generate($name, $id, $data)
    {
        $hashKey = app('encrypter')->getKey();

        $stringForHashing = $name . $id . json_encode($data);

        \Log::debug($stringForHashing); // <= Add this

        return hash_hmac('sha256', $stringForHashing, $hashKey);
    }

Then you can check in your log file (or Laravel Telescope), to see the output. I simply used Text Compare to compare the different request outputs.

calebporzio commented 5 years ago

This should be fixed in v0.3.5 - reopen if issue persists. Thanks!

oliverbj commented 5 years ago

Unfortunately, v.0.3.5 did not fix the problem for me - or at least, it created another problem.

Consider below Liveview view component, with a model attached:

<input type="text"
               wire:model.lazy="arguments.search"
               value="{{$arguments['search']}}"
               placeholder="Search for...">

And inside the component:

public $arguments = [];

public function mount($rule)
    {
        $this->rule = $rule;
        $this->arguments = $rule['arguments'];
    }

If I dd($this->arguments) inside the mount() method, I also see that they are populated:

array:2 [▼
  "search" => "Page"
  "replace" => "Google"
]

And finally, when I view the actual source code:

<input type="text" wire:model.lazy="arguments.search" value="Page" placeholder="Search for..." >

You can see, that the value= have the value of Page. However, this value is not visible in the actual input bar:

enter image description here

If I remove the wire:model.lazy from the input field, the value is shown:

enter image description here

oliverbj commented 5 years ago

Further, when trying to trigger any of the methods on the inputs, I get:

Illuminate \ Contracts \ Encryption \ DecryptException

The payload is invalid.

Without changing anything and then downgrading to 0.3.4, the above code works (the value= is shown and I can fire off the methods).

I even tried to comment out the changes in the LivewireServiceProvider that you did in v.0.3.5, but that didn't fix it either. So not quite sure what is causing this?

oliverbj commented 5 years ago

@calebporzio not sure on how to re-open this issue?

kossa commented 4 years ago

I have the same issue with v1.0.9, any solutions ?

GertjanRoke commented 4 years ago

@kossa do you maybe have a public property that is an array that has keys that can be a string or a numeric character?

Like this for example?

public $array = [
    'foo' => 'bar',
    '123' => 500
]

I think this is creating a problem because Javascript handles it differently.

kossa commented 4 years ago

For my case I found the problem, I used __construct instead of mount function to init variables

GertjanRoke commented 4 years ago

@kossa I was also looking into this for somebody else and maybe this is also the case on your side. I wrote a the explaining of why you can get that error because of public array property: https://github.com/livewire/livewire/issues/821#issuecomment-615939560

kossa commented 4 years ago

Excellent thank you

prathamesh-gharat commented 3 years ago

In general this error can occur because of difference in data between PHP and JavaScript for various reasons.

One such case was difference in supposed MAX INT in PHP vs JS.

PHP MAX INT: 9223372036854775807
 JS MAX INT: 9007199254740991

Ref: https://r00t.io/laravel/livewire-corruptcomponentpayloadexception/