title: Vanilla AJAX request passes Laravel validation but values aren't available in the controller
body: <p>I'm trying to use AJAX (vanilla JS) to submit a form to a Laravel 5.5 controller that will search the Amazon products API.</p>
  
  <p>The AJAX is submitting the correct keywords and category inputs and the request is passing a form request that requires both keywords and category, but the keywords and category are not available in the controller. I do get a placeholder response from the controller.</p>
  
  <p>The request payload in Chrome's Network tab in Developer Tools shows the AJAX is sending the correct terms.</p>
  
  <p>HTML form</p>
  
  <pre><code>&lt;form method="POST" action="http://local.example.com/api/products/search" accept-charset="UTF-8" id="product-search-form"&gt;
      &lt;input name="_token" type="hidden" value="qcXA3xVIoltX9tSpeJeWuKVa3alUkq1p5tuIlKJ5"&gt;
      &lt;div class="form-group"&gt;
          &lt;label for="keywords" class="required"&gt;Keywords&lt;/label&gt; 
          &lt;input id="keywords" placeholder="Enter keywords" aria-required name="keywords" type="text"&gt;
      &lt;/div&gt;
      &lt;div class="form-group "&gt;
          &lt;label for="category"&gt;Category&lt;/label&gt; 
          &lt;select id="category" name="category"&gt;
              &lt;option selected="selected" value=""&gt;Choose a category...&lt;/option&gt;
              &lt;option value="All"&gt;All&lt;/option&gt;
              &lt;option value="Apparel"&gt;Apparel&lt;/option&gt;
              &lt;option value="Books"&gt;Books&lt;/option&gt;
              &lt;option value="DVD"&gt;DVD&lt;/option&gt;
              &lt;option value="Electronics"&gt;Electronics&lt;/option&gt;
              &lt;option value="Music"&gt;Music&lt;/option&gt;
              &lt;option value="Software"&gt;Software&lt;/option&gt;
              &lt;option value="SoftwareVideoGames"&gt;SoftwareVideoGames&lt;/option&gt;
              &lt;option value="Toys"&gt;Toys&lt;/option&gt;
              &lt;option value="VideoGames"&gt;VideoGames&lt;/option&gt;
              &lt;option value="UnboxVideo"&gt;Video on Demand&lt;/option&gt;
          &lt;/select&gt;
      &lt;/div&gt;
      &lt;div&gt;
          &lt;button id="search-products-submit" type="submit" class="button-primary"&gt;Search&lt;/button&gt;
      &lt;/div&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>JavaScript AJAX post request on submit</p>
  
  <pre><code>function getProducts(keywords, category) {
      let path = "api/products/search";
  
      let searchTerms = {
          keywords: keywords,
          category: category,
          _token: getCsrfToken(),
      };
  
      ajaxPostRequest(path, showProducts, searchTerms);
  }
  </code></pre>
  
  <p>AJAX requests</p>
  
  <pre><code>function ajaxPostRequest(path, callback, formId) {
      return ajaxRequest("POST", path, callback, formId);
  }
  
  function ajaxRequest(method, path, callback, data = null) {
      let xhr = new XMLHttpRequest();
      let url = getFullUrl(path);
  
      addEventListeners(xhr);
  
      xhr.open(method, url, true);
  
      xhr.onreadystatechange = function() {
          if (xhr.readyState === 4 &amp;&amp; xhr.status === 200) {
              let parsedResponse = JSON.parse(xhr.responseText);
              callback(parsedResponse);
          }
      };
  
      setRequestHeaders(xhr, method);
  
      xhr.send(JSON.stringify(data));
  }
  </code></pre>
  
  <p>AJAX request payload</p>
  
  <pre><code>{
      keywords: "aaa",
      category: "All", 
      _token: "qcXA3xVIoltX9tSpeJeWuKVa3alUkq1p5tuIlKJ5"
  }
  </code></pre>
  
  <p>Form request</p>
  
  <pre><code>class SearchProductsFormRequest extends Request
  {
      /**
       * Determine if the user is authorized to make this request
       *
       * @return bool
       */
      public function authorize()
      {
          return true;
      }
  
      /**
       * Get the validation rules that apply to the request
       *
       * @return array
       */
      public function rules(): array
      {
          return [
              'keywords' =&gt; 'required',
              'category' =&gt; 'required'
          ];
      }
  }
  </code></pre>
  
  <p>Controller</p>
  
  <pre><code>/**
   * @param SearchProductsFormRequest $request
   * @return JsonResponse
   */
  public function create(SearchProductsFormRequest $request): JsonResponse
  {
      if ($request-&gt;has('keywords')) {
          \Log::info('Got keywords');
      } else {
          \Log::info('Did not get keywords');
      }
  
      if ($request-&gt;has('keywords')) {
          \Log::info('Got category');
      } else {
          \Log::info('Did not get category');
      }
  
      \Log::info(print_r($request, true));
      \Log::info('keywords: ' . $request-&gt;input('keywords'));
      \Log::info('category: ' . $request-&gt;input('category'));
      return response()-&gt;json([
          'name' =&gt; 'Abigail',
          'state' =&gt; 'CA'
      ]);
  }
  </code></pre>
  
  <p>Log output</p>
  
  <pre><code>[2018-04-08 19:26:15] local.INFO: Did not get keywords
  [2018-04-08 19:26:15] local.INFO: Did not get category
  [2018-04-08 19:26:15] local.INFO: keywords:
  [2018-04-08 19:26:15] local.INFO: category:
  </code></pre>
  
  <p>console.log of server response (dummy data for testing)</p>
  
  <pre><code>{name: "Abigail", state: "CA"}
  </code></pre>
  
  <p>So the controller is getting called and responding, but can't read the input data.</p>
  
  <p>setRequestHeaders()</p>
  
  <pre><code>function setRequestHeaders(xhr, method) {
      xhr.setRequestHeader('Accept', 'application/json');
      xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
      xhr.setRequestHeader("X-CSRF-TOKEN", getCsrfToken());
  
  if (method === 'GET') {
      xhr.setRequestHeader('Content-Type', 'application/json');
  } else {
      xhr.setRequestHeader('Content-Type', 'multipart/form-data');
  }
  </code></pre>