Answers.Body: <p>I solved my issue, but unfortunately at this point in time I don't fully understand <em>why</em> this resolves the problem.</p>
  
  <p>My controller, its <code>behaviors</code> method, and my frontend request were all correct. <strong>The problem was with two Axios defaults I had set</strong> in my external application that was sending the request to my Craft install:</p>
  
  <pre class="lang-js prettyprint-override"><code>// Axios.defaults.headers.common['Accept'] = 'application/json';
  // Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
  </code></pre>
  
  <p>Once these were commented-out or removed my external Vue application was able to successfully send its request to my Craft controller and receive an array of entries.</p>
  
  <p>As for the <code>behaviors</code> method in my Controller I only needed one <code>corsFilter</code>:</p>
  
  <pre class="lang-php prettyprint-override"><code>public function behaviors ()
  {
      return [
          'corsFilter' =&gt; [
              'class' =&gt; Cors::class,
              'cors' =&gt; [
                  'Origin' =&gt; ['http://localhost:8080'] // dev url of external app
              ]
          ]
      ];
  }
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-04-30T14:05:04.863Z
Answers.LastActivityDate: 2019-04-30T15:08:53.363Z
Answers.LastEditDate: 2019-04-30T15:08:53.363Z
Answers.UserDisplayName: Steven Thate
Body: <p>Craft 3.1.18</p>
  
  <p>I have a simple controller <code>VeteransController</code> made using the updated Business Logic module. The purpose of this controller is to create a very simple endpoint for an external Vue application to send requests to and receive an array of <code>veterans</code> entries.</p>
  
  <p>When I test this controller using a "local" version of the same Vue component it functions perfectly. The response includes an array of entries just as expected.</p>
  
  <p><strong>Error from console</strong></p>
  
  <p>But when I send the request from my external Vue app I get a 404 error: </p>
  
  <pre><code>Access to XMLHttpRequest at 'https://myproject.test/actions/business-logic/veterans/index' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
  </code></pre>
  
  <p>This seems to suggest that the action isn't sending the correct status code with the response or doesn't know how to correctly respond to the preflight OPTIONS. But the logs from <code>storage/logs</code> <em>seem</em> to tell a different story.</p>
  
  <p><strong>Error from storage/logs/web-404s.log:</strong></p>
  
  <pre><code>[yii\web\Application::handleRequest] Route requested: 'templates/render'
  [yii\base\Controller::runAction] Route to run: templates/render
  [yii\base\InlineAction::runWithParams] Running action: craft\controllers\TemplatesController::actionRender()
  [yii\web\HttpException:404] yii\web\NotFoundHttpException: Template not found: actions/business-logic/veterans/index in /Users/steven/Code/destination-small-town/vendor/craftcms/cms/src/controllers/TemplatesController.php:70
  </code></pre>
  
  <p>This is my first time getting this deep into extending Craft like this, so I could be missing small steps or reading things incorrectly. But those errors make it sound like Craft is trying to read the URL from my Vue's axios post call (<code>https://mycraftproject.test/actions/business-logic/veterans/index</code>) and render a template that doesn't exist rather than navigate to the correct controller action. </p>
  
  <p>But when I place a breakpoint in PhpStorm on my <code>VeteransController</code> class definition and go through the steps I can tell that the request is indeed making its way to the controller. But when it goes along down the steps to the <code>runAction()</code> method in <code>yii\base\Module</code> it returns a <code>null</code> value.</p>
  
  <p><strong>So I can establish that my request is reaching the controller. But it is not reaching my index action. I'm at a loss as to how I should proceed or troubleshoot further.</strong></p>
  
  <p>Here is my controller in case I am missing something simple here:</p>
  
  <pre class="lang-php prettyprint-override"><code>class VeteransController extends Controller
  {
  
      public $allowAnonymous = true;
      public $enableCsrfValidation = false;
  
      /**
       * Add CORS Filter from Yii2
       */
      public function behaviors()
      {
          return [
              'corsFilter' =&gt; [
                  'class' =&gt; \yii\filters\Cors::className(),
                  'cors' =&gt; [
                      'Origin' =&gt; ['*'],
                      'Access-Control-Request-Method' =&gt; ['POST', 'OPTIONS'],
                      'Access-Control-Request-Headers' =&gt; ['*'],
                  ]
              ]
          ];
      }
  
      /**
       * @throws BadRequestHttpException
       */
      public function actionIndex ()
      {
          $this-&gt;requirePostRequest();
          $this-&gt;requireAcceptsJson();
          $request = Craft::$app-&gt;getRequest();
  
          $countyId = $request-&gt;getBodyParam('countyId');
          $veterans = Entry::find()
              -&gt;section('veterans')
              -&gt;relatedTo($countyId)
              -&gt;limit(10)
              -&gt;all();
  
  
          return $this-&gt;asJson([
              'success' =&gt; true,
              'veterans' =&gt; $veterans
          ]);
      }
  
  }
  </code></pre>
  
  <p><strong>Request Headers:</strong></p>
  
  <p>The request headers are set by my Axios instance in my external Vue app.</p>
  
  <pre class="lang-js prettyprint-override"><code>Axios.defaults.headers.common['Accept'] = 'application/json';
  Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
  Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  </code></pre>
  
  <p>While not set as an Axios default the request does include <code>Access-Control-Request-Method</code> with a value of <code>POST</code>.</p>
  
  <p>In Chrome's dev tools, under the Network -> Headers tabs and below the first section which is called "General" it shows the following:</p>
  
  <pre><code>Request URL: https://destinationsmalltown.test/actions/business-logic/veterans/index
  Request Method: OPTIONS
  Status Code: 404 Not Found
  Remote Address: [::1]:443
  Referrer Policy: no-referrer-when-downgrade
  </code></pre>
  
  <p>Thank you!</p>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-04-17T19:54:37.400Z
Comments.Text: Can you show your request headers? Do they contain `Access-Control-Request-Method`
Comments.UserDisplayName: Robin Schambach
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2019-04-17T20:04:55.447Z
Comments.Text: I updated the question with request header info and also "general" header info found in dev tools. The request is post, but it's returning a status of 404 during the OPTIONS method, which is some kind of preflight before the real post? This CORS stuff is new to me so I'm trying to wrap my head around it!
Comments.UserDisplayName: Steven Thate
Tags: <plugin-development><controller><modules><request>
Title: How do I correctly send an external request to a module controller? runAction() is returning null
ThreadText: # How do I correctly send an external request to a module controller? runAction() is returning null
  
  (Asked by: username_0 on 2019-04-17)
  
  Craft 3.1.18
  
  I have a simple controller `VeteransController` made using the updated Business Logic module. The purpose of this controller is to create a very simple endpoint for an external Vue application to send requests to and receive an array of `veterans` entries.
  
  When I test this controller using a "local" version of the same Vue component it functions perfectly. The response includes an array of entries just as expected.
  
  **Error from console**
  
  But when I send the request from my external Vue app I get a 404 error:
  
  ```
  Access to XMLHttpRequest at 'https://myproject.test/actions/business-logic/veterans/index' from origin 'http://localhost:8080' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
  
  ```
  
  This seems to suggest that the action isn't sending the correct status code with the response or doesn't know how to correctly respond to the preflight OPTIONS. But the logs from `storage/logs` *seem* to tell a different story.
  
  **Error from storage/logs/web-404s.log:**
  
  ```
  [yii\web\Application::handleRequest] Route requested: 'templates/render'
  [yii\base\Controller::runAction] Route to run: templates/render
  [yii\base\InlineAction::runWithParams] Running action: craft\controllers\TemplatesController::actionRender()
  [yii\web\HttpException:404] yii\web\NotFoundHttpException: Template not found: actions/business-logic/veterans/index in /Users/username_0/Code/destination-small-town/vendor/craftcms/cms/src/controllers/TemplatesController.php:70
  
  ```
  
  This is my first time getting this deep into extending Craft like this, so I could be missing small steps or reading things incorrectly. But those errors make it sound like Craft is trying to read the URL from my Vue's axios post call (`https://mycraftproject.test/actions/business-logic/veterans/index`) and render a template that doesn't exist rather than navigate to the correct controller action.
  
  But when I place a breakpoint in PhpStorm on my `VeteransController` class definition and go through the steps I can tell that the request is indeed making its way to the controller. But when it goes along down the steps to the `runAction()` method in `yii\base\Module` it returns a `null` value.
  
  **So I can establish that my request is reaching the controller. But it is not reaching my index action. I'm at a loss as to how I should proceed or troubleshoot further.**
  
  Here is my controller in case I am missing something simple here:
  
  ```php
  class VeteransController extends Controller
  {
  
      public $allowAnonymous = true;
      public $enableCsrfValidation = false;
  
      /**
       * Add CORS Filter from Yii2
       */
      public function behaviors()
      {
          return [
              'corsFilter' => [
                  'class' => \yii\filters\Cors::className(),
                  'cors' => [
                      'Origin' => ['*'],
                      'Access-Control-Request-Method' => ['POST', 'OPTIONS'],
                      'Access-Control-Request-Headers' => ['*'],
                  ]
              ]
          ];
      }
  
      /**
       * @throws BadRequestHttpException
       */
      public function actionIndex ()
      {
          $this->requirePostRequest();
          $this->requireAcceptsJson();
          $request = Craft::$app->getRequest();
  
          $countyId = $request->getBodyParam('countyId');
          $veterans = Entry::find()
              ->section('veterans')
              ->relatedTo($countyId)
              ->limit(10)
              ->all();
  
          return $this->asJson([
              'success' => true,
              'veterans' => $veterans
          ]);
      }
  
  }
  
  ```
  
  **Request Headers:**
  
  The request headers are set by my Axios instance in my external Vue app.
  
  ```js
  Axios.defaults.headers.common['Accept'] = 'application/json';
  Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
  Axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
  
  ```
  
  While not set as an Axios default the request does include `Access-Control-Request-Method` with a value of `POST`.
  
  In Chrome's dev tools, under the Network -> Headers tabs and below the first section which is called "General" it shows the following:
  
  ```
  Request URL: https://destinationsmalltown.test/actions/business-logic/veterans/index
  Request Method: OPTIONS
  Status Code: 404 Not Found
  Remote Address: [::1]:443
  Referrer Policy: no-referrer-when-downgrade
  
  ```
  
  Thank you!
  
  **Tags:** plugin-development, controller, modules, request
  
  > **Comments:**
  > **username_1**: Can you show your request headers? Do they contain `Access-Control-Request-Method`  
  > **username_0**: I updated the question with request header info and also "general" header info found in dev tools. The request is post, but it's returning a status of 404 during the OPTIONS method, which is some kind of preflight before the real post? This CORS stuff is new to me so I'm trying to wrap my head around it!  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2019-04-30. Score: 1 | Accepted answer
  
  I solved my issue, but unfortunately at this point in time I don't fully understand *why* this resolves the problem.
  
  My controller, its `behaviors` method, and my frontend request were all correct. **The problem was with two Axios defaults I had set** in my external application that was sending the request to my Craft install:
  
  ```js
  // Axios.defaults.headers.common['Accept'] = 'application/json';
  // Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
  
  ```
  
  Once these were commented-out or removed my external Vue application was able to successfully send its request to my Craft controller and receive an array of entries.
  
  As for the `behaviors` method in my Controller I only needed one `corsFilter`:
  
  ```php
  public function behaviors ()
  {
      return [
          'corsFilter' => [
              'class' => Cors::class,
              'cors' => [
                  'Origin' => ['http://localhost:8080'] // dev url of external app
              ]
          ]
      ];
  }
  
  ```
  
  
  ---
  
  