1

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/steven/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:

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.

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!

  • 1
    Can you show your request headers? Do they contain Access-Control-Request-Method – Robin Schambach Apr 17 at 19:54
  • 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! – Steven Thate Apr 17 at 20:04
1

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:

// 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:

public function behaviors ()
{
    return [
        'corsFilter' => [
            'class' => Cors::class,
            'cors' => [
                'Origin' => ['http://localhost:8080'] // dev url of external app
            ]
        ]
    ];
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.