2
votes

I'm having trouble with an AJAX POST request in Laravel 5.2. I'm passing a valid CSRF token in my AJAX request.

Opening and closing the form using Laravel Collective HTML package form tags, which automatically adds a hidden _token input with the CSRF token. Rendered HTML:

<form method="POST" action="http://www.example.com/admin/products/search" accept-charset="UTF-8" id="product-search-form">
<input name="_token" type="hidden" value="mOaBxU1sVUsRX1KkuAeVhSDSxj0LKT8DDxl9USQc">

<div>
    <label for="keywords" class="required">Keywords</label>
    <input id="keywords" placeholder="Enter keywords" name="keywords" type="text">
</div>

<div>
    <label for="category" class="required">Category</label>
    <select id="category" name="category">
        <option selected="selected" value="">Choose a category...</option>
        <option value="category-1">Category 1</option>
        <option value="category-2">Category 2</option>
        <option value="category-3">Category 3</option>
    </select>
</div>

<div>
    <button id="search-product-submit" type="submit">Search</button>
</div>

</form>

Using Fetch API and FormData to do AJAX request:

function $(id) {
    return document.getElementById(id);
}

var searchProductSubmitButton = $("search-product-submit");

function fetchStatus(response) {
    if (response.status >= 200 && response.status < 300) {
        return Promise.resolve(response);
    } else {
        return Promise.reject(new Error(response.statusText));
    }
}

function fetchJson(response) {
    return response.json();
}

function searchProducts(evt){
    var keywords = $("keywords").value,
        categorySelect = $("category"),
        category = categorySelect.options[categorySelect.selectedIndex].value,
        csrfToken = document.getElementsByTagName("meta")["csrf-token"].getAttribute("content"),
        productSearchForm = $("product-search-form"),
        formData = new FormData(productSearchForm);

    evt.preventDefault();

    if(keywords !== "" && category !== ""){
        fetch("/admin/products/search", {
            method: "POST",
            body: formData,
            headers: {
                "X-CSRF-TOKEN": csrfToken
            }
        })
        .then(fetchStatus)
        .then(fetchJson)
        .then(function(products) {
            console.log("The operation was a complete success");
        }).catch(function(error) {
            console.log("Request failed", error);
        });
    }
}
searchProductSubmitButton.addEventListener("click", searchProducts, false);
searchProductSubmitButton.addEventListener("keypress", searchProducts, false);

The CSRF token and form data are in the request payload:

------WebKitFormBoundaryQFECPn7xpptqi076
Content-Disposition: form-data; name="_token"

mOaBxU1sVUsRX1KkuAeVhSDSxj0LKT8DDxl9USQc
------WebKitFormBoundaryQFECPn7xpptqi076
Content-Disposition: form-data; name="keywords"

dsafafa
------WebKitFormBoundaryQFECPn7xpptqi076
Content-Disposition: form-data; name="category"

DVD
------WebKitFormBoundaryQFECPn7xpptqi076--

The _token input in the request payload and the x-csrf-token header are the same as the token seen in View Source.

Route is using web middleware:

Route::group(['prefix' => 'admin', 'middleware' => 'web'], function () {
    Route::post('products/search', [ // == "admin/products/search" with prefix
        'as'   => 'products.search',
        'uses' => 'ProductsController@search'
    ]);
});

Controller:

public function search(SearchRequest $request){
    Log::info('keywords: ' . $request->keywords);
    Log::info('category: ' . $request->category);

    return response()->json([
        'keywords' => $request->keywords,
        'category' => $request->category
    ], 200);
}
2
Were there any requests to your application in between receiving the token and submitting it?Boyd
Nope, I'm on my local development environment and only have the site open in one browser tab.kirkaracha
Are you sure you are not generating it twice? Once for the form element and once for the meta tag?Boyd
The meta tag and hidden form field are identical.kirkaracha

2 Answers

0
votes

I had these problems sometimes too and fixed it by sending a POST with _token instead of putting it in the headers.

Or why don't you serialize the whole form?