1

I have a list of comments with a post and I want a user to be able to edit their post if they want to. In the list there's a edit button (if the post belongs to the user logged in) and when clicked an ajax-request is made and a view is returned. This view contains the form to edit the comment and is preloaded with the comment-details. To this point all works well.

Now here's the part where I'm running into an unclear problem. When I want to submit the form, using a PATCH request, it fails with an error 500. The structure looks like this:

  • A user visits /posts/{id} In this view all comments are listed.

  • When a user clicks the edit button /posts/{id}/comments/{comment_id}/edit is called, the view with the form is returned

  • When the user submits this form Route::patch('/posts/{id}/comments/{comment_id}/edit', ['as' => 'posts.editComment', 'uses' => 'PostsController@update'] is triggered. In there is fails.

Using the Network inspector in Chrome I'm able to click the error. In there it tells me there's a token mismatch? I'm not modifying any tokens, just using the default form. The form code is below:

{!! Form::model($comment, ['method' => 'PATCH', 'route' => ['posts.editComment', $comment->post_id, $comment->id], 'id' => 'editComment' . $comment->id]) !!}
    <div class="comment-edit">
        {!! Form::textarea('comment', null, ['class' => 'form-control', 'style' => 'height: 80px;']) !!}
    </div>
    <div class="comment-edit-buttons text-right">
        {!! Form::button(trans('general.cancel'), ['class' => 'btn btn-default btn-sm cancelEditComment', 'data-postId' => $comment->post_id] ) !!}
        {!! Form::button(trans('general.edit'), ['class' => 'btn btn-primary btn-sm  editComment', 'data-postId' => $comment->post_id, 'data-commentId' => $comment->id] ) !!}
    </div>
{!! Form::close() !!}

Which translates to the browser like:

<form method="POST" action="http://www.domain.com/posts/29/comments/12/edit" accept-charset="UTF-8" id="editComment12">
    <input name="_method" type="hidden" value="PATCH">
    <input name="_token" type="hidden" value="generated token value">
    <div class="comment-edit">
        <textarea class="form-control" style="height: 80px;" name="comment" cols="50" rows="10">nieuwe comment</textarea>
    </div>
    <div class="comment-edit-buttons text-right">
        <button class="btn btn-default btn-sm cancelEditComment" data-postId="29" type="button">Annuleren</button>
        <button class="btn btn-primary btn-sm  editComment" data-postId="29" data-commentId="12" type="button">Bewerken</button>
    </div>
</form>

I'm not really sure where to look? Is there a special way to send PATCH request using ajax?

Ben Fransen
  • 10,884
  • 18
  • 76
  • 129
  • 1
    I found this discussion on Laracasts, scroll down to Lukas post, he discusses the issue of an unencrypted token issue, which is what you may be having trouble with. https://laracasts.com/discuss/channels/general-discussion/csrf-form-token-doesnt-match-session-token – Azeame Apr 17 '15 at 13:43
  • Thanks, I'll take a look right away and let you know if it helped. – Ben Fransen Apr 17 '15 at 13:47
  • I think your link helped to get a little further. But now I'm getting a 405 - Method not allowed. While the route is defined and the controller method exists..? – Ben Fransen Apr 17 '15 at 14:09
  • what have you changed? – Fabio Antunes Apr 17 '15 at 14:19
  • I've replaced the Form-plugin with a hard-coded
    tag because of the encryption of the csrf token. In addition I've extended the ajax-call with a headers X-XSRF-TOKEN entry.
    – Ben Fransen Apr 17 '15 at 14:21
  • 1
    Well you should revert to what you had, {{ Form::open() }} clearly that was right – Fabio Antunes Apr 17 '15 at 14:28
  • Hmm I believe I still haven't fixed the token issue. Never mind the 405 issue I'm having. I'll dive deeper into the token encryption. – Ben Fransen Apr 17 '15 at 14:29
  • could you also post the code responsible for your ajax request? – Fabio Antunes Apr 17 '15 at 14:32
  • I've fixed the issue. I misunderstood the purpose for the encrypted token. At first I've replaced the hidden _token input with the encrypted token. But later I figured (also because of your comment that my {!! Form:: ... !!} was correct) I only had to add an extra input for the X-XSRF-TOKEN header. It's working now! Thanks to the both of you! :) @Azeame, if you post the link as an answer I will accept it. +1 for the comment already! For you as wel Fabio Antunes – Ben Fransen Apr 17 '15 at 14:41

1 Answers1

1

Your problem has to do with the encryption of the csfr_token, in your blade file add the following:

$encrypter = app('Illuminate\Encryption\Encrypter');
$encrypted_token = $encrypter->encrypt(csrf_token());

then add the following field to your form:

<input id="token" type="hidden" value="{{$encrypted_token}}">

and finally be sure to add the original csfr token to the headers as follows:

<script>
.....
var $_token = $('#token').val();
....
$.ajax({
                type: 'post',
                cache: false,
                headers: { 'X-XSRF-TOKEN' : $_token }, 
                url: 'the_url_to_controller_thru_route/' + some_parameters_if_needed,
                //contentType: "application/json; charset=utf-8",
                //dataType: 'json',
                data: {comment_id: 873}, //assuming that you send some data like id of a comment to controller 
                                success: function(data) {
....
</script>

Ben, if you notice something wrong please edit my answer, it's best for this to be as accurate as possible.

Azeame
  • 2,322
  • 2
  • 14
  • 30
  • This is correct, apart from the fact that my ajax-call looks different. But if someone runs into the same issue later on this should point them towards the solution :) Cheers! – Ben Fransen Apr 18 '15 at 10:52