title: Lumen API CORS Ajax 405 Method Not Allowed
body: <p>I have an api on Laravel Lumen, we test via Postman and Ruby Rest Client and all go very well, but we create a simple Auth Login that response a web token, all works fine but on our React App actually we have this "405 405 Method Not Allowed".</p>
  
  <p>We create a class with the next code:</p>
  
  <pre><code>&lt;?php namespace palanik\lumen\Middleware;
  
  use Closure;
  use Illuminate\Http\Response;
  
  class LumenCors {
  
      protected $settings = array(
                  'origin' =&gt; '*',    // Wide Open!
                  'allowMethods' =&gt; 'GET,HEAD,PUT,POST,DELETE,PATCH,OPTIONS',
                  );
  
      protected function setOrigin($req, $rsp) {
          $origin = $this-&gt;settings['origin'];
          if (is_callable($origin)) {
              // Call origin callback with request origin
              $origin = call_user_func($origin,
                                      $req-&gt;header("Origin")
                                      );
          }
          $rsp-&gt;header('Access-Control-Allow-Origin', $origin);
      }
  
      protected function setExposeHeaders($req, $rsp) {
          if (isset($this-&gt;settings['exposeHeaders'])) {
              $exposeHeaders = $this-&gt;settings['exposeHeaders'];
              if (is_array($exposeHeaders)) {
                  $exposeHeaders = implode(", ", $exposeHeaders);
              }
  
              $rsp-&gt;header('Access-Control-Expose-Headers', $exposeHeaders);
          }
      }
  
      protected function setMaxAge($req, $rsp) {
          if (isset($this-&gt;settings['maxAge'])) {
              $rsp-&gt;header('Access-Control-Max-Age', $this-&gt;settings['maxAge']);
          }
      }
  
      protected function setAllowCredentials($req, $rsp) {
          if (isset($this-&gt;settings['allowCredentials']) &amp;&amp; $this-&gt;settings['allowCredentials'] === True) {
              $rsp-&gt;header('Access-Control-Allow-Credentials', 'true');
          }
      }
  
      protected function setAllowMethods($req, $rsp) {
          if (isset($this-&gt;settings['allowMethods'])) {
              $allowMethods = $this-&gt;settings['allowMethods'];
              if (is_array($allowMethods)) {
                  $allowMethods = implode(", ", $allowMethods);
              }
  
              $rsp-&gt;header('Access-Control-Allow-Methods', $allowMethods);
          }
      }
  
      protected function setAllowHeaders($req, $rsp) {
          if (isset($this-&gt;settings['allowHeaders'])) {
              $allowHeaders = $this-&gt;settings['allowHeaders'];
              if (is_array($allowHeaders)) {
                  $allowHeaders = implode(", ", $allowHeaders);
              }
          }
          else {  // Otherwise, use request headers
              $allowHeaders = $req-&gt;header("Access-Control-Request-Headers");
          }
  
          if (isset($allowHeaders)) {
              $rsp-&gt;header('Access-Control-Allow-Headers', $allowHeaders);
          }
      }
  
      protected function setCorsHeaders($req, $rsp) {
  
          // http://www.html5rocks.com/static/images/cors_server_flowchart.png
          // Pre-flight
          if ($req-&gt;isMethod('OPTIONS')) {
              $this-&gt;setOrigin($req, $rsp);
              $this-&gt;setMaxAge($req, $rsp);
              $this-&gt;setAllowCredentials($req, $rsp);
              $this-&gt;setAllowMethods($req, $rsp);
              $this-&gt;setAllowHeaders($req, $rsp);
          }
          else {
              $this-&gt;setOrigin($req, $rsp);
              $this-&gt;setExposeHeaders($req, $rsp);
              $this-&gt;setAllowCredentials($req, $rsp);
          }
      }
  
      /**
       * Handle an incoming request.
       *
       * @param  \Illuminate\Http\Request  $request
       * @param  \Closure  $next
       * @return mixed
       */
      public function handle($request, Closure $next) {
  
          if ($request-&gt;isMethod('OPTIONS')) {
              $response = new Response("", 200);
          }
          else {
              $response = $next($request);
          }
  
          $this-&gt;setCorsHeaders($request, $response);
  
          return $response;
      }
  
  }
  </code></pre>
  
  <p>We add on bootstrap/app.php</p>
  
  <pre><code>$app-&gt;routeMiddleware([
      'auth' =&gt; App\Http\Middleware\Authenticate::class,
      'cors' =&gt; palanik\lumen\Middleware\LumenCors::class,
  ]);
  </code></pre>
  
  <p>And our route:</p>
  
  <pre><code>$app-&gt;group(['middleware' =&gt; 'cors'], function($app)
  {
      $app-&gt;post('/auth/login', function() {
          return response()-&gt;json([
              'message' =&gt; 'CORS OPTIONS Accepted.',
          ]);
      });
  });
  </code></pre>
  
  <p>With our rest clients all goes fine but how i said on our React js app because the browser send an OPTIONS, the request doesn't pass to the POST.</p>
  
  <p>Our Recat Axios request:</p>
  
  <pre><code>axios({
      method: 'post',
      url: 'URL',
      data: {
              email: 'Fred',
              password: 'Flintstone'
      }
      }).then(function (response) {
          console.log(response);
      })
      .catch(function (error) {
          console.log(error);
      });
  </code></pre>
  
  <p>Error con React log:
  The problem is that OPTIONS is send before POST for the browser.
  Headers response:
      HTTP/1.1 405 Method Not Allowed
      Allow: POST
      Cache-Control: no-cache, private
      Content-Type: text/html; charset=UTF-8
      Date: Sun, 29 Oct 2017 01:30:34 GMT
      Server: Apache/2.4.18 (Ubuntu)
      transfer-encoding: chunked
      Connection: keep-alive</p>
  
  <p>What are we missing on our API?</p>
  
  <p>Regards.</p>