Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
997 views
in Technique[技术] by (71.8m points)

cakephp - CORS errors because of internal app error

I am creating a simple API with CakePHP 4, and I am having some issues with some CORS requests.

Access to XMLHttpRequest at 'http://localhost/myapp.api/elaborations/add.json' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

Everything works with every other request and after some digging I've found that the error was an undefined index in my controller. If I fix that, the CORS error disappears. I just didn't see it in my log files, it's my bad.

It's a bit confusing seeing a CORS error because of a coding error though. I guess the issue could be in my CORS configuration, and hence this question.

This is what I've ended with, after a little bit of web search, trials and errors. I know it's ugly but I couldn't find anything better that actually worked.

How can I avoid having CORS errors for coding issues? I guess there is some redirect action somewhere, but I can't figure out how to avoid it.

<?php
namespace AppMiddleware;

use PsrHttpMessageResponseInterface;
use PsrHttpMessageServerRequestInterface;
use PsrHttpServerRequestHandlerInterface;
use PsrHttpServerMiddlewareInterface;

class CorsMiddleware implements MiddlewareInterface
{
    public function process(
        ServerRequestInterface $request,
        RequestHandlerInterface $handler
    ): ResponseInterface
    {
        // Calling $handler->handle() delegates control to the *next* middleware
        // In your application's queue.
        $response = $handler->handle($request);

        if ($request->getHeader('Origin')) {
            $allowedDomains = [
                'https://myapp.it',
                'https://www.myapp.it',
                'http://localhost:3000',
            ];
            $origin = $_SERVER['HTTP_ORIGIN'];

            if (in_array($origin, $allowedDomains)) {
                header('Access-Control-Allow-Origin: ' . $origin);
            }

            header('Access-Control-Allow-Methods: POST, GET, PUT, PATCH, DELETE, OPTIONS');
            header('Access-Control-Allow-Headers: *');

            if (strtoupper($request->getMethod()) === 'OPTIONS') {
                exit(0);
            }
        }

        return $response;
    }
}
question from:https://stackoverflow.com/questions/65951557/cors-errors-because-of-internal-app-error

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

First of all, do not access superglobals in CakePHP directly, always use the abstracted APIs! Also you shouldn't echo data manually, that includes headers, again, use the abstracted APIs! Using superglobals and echoing data will only get you in trouble, it messes up the testing environment, it can lead to data not being sent (properly), etc.

That being said, for proper CORS coverage you also need to modify error handling, as the error controller will create a new response instance. You should be able to process the request/response in a custom exception renderer, at ExceptionRenderer::_getController(), like so:

// in src/Error/AppExceptionRenderer.php
namespace AppError;

use AppHttpMiddlewareCorsMiddleware;
use CakeControllerController;
use CakeErrorExceptionRenderer;

class AppExceptionRenderer extends ExceptionRenderer
{
    protected function _getController(): Controller
    {
        $controller = parent::_getController();

        $cors = new CorsMiddleware();
        $response = $cors->setHeaders(
            $controller->getRequest(),
            $controller->getResponse()
        );

        return $controller->setResponse($response);
    }
}
// in config/app.php
'Error' => [
    'exceptionRenderer' => AppErrorAppExceptionRenderer::class,
    // ...
],

Your CORS middleware would supply the setHeaders() method accordingly, where you set the CORS headers if required, based on your example it could look something like this:

public function process(
    ServerRequestInterface $request,
    RequestHandlerInterface $handler
): ResponseInterface
{
    $response = $handler->handle($request);
    $response = $this->setHeaders($request, $response);

    return $response;
}


public function setHeaders(
    ServerRequestInterface $request,
    ResponseInterface $response
): ResponseInterface
{
    if ($request->getHeader('Origin')) {
        $allowedDomains = [
            'https://myapp.it',
            'https://www.myapp.it',
            'http://localhost:3000',
        ];
        
        $origins = $request->getHeader('Origin');
        $lastOrigin = end($origins);
        if (in_array($lastOrigin, $allowedDomains, true)) {
            $response = $response
                ->withHeader('Access-Control-Allow-Origin', $lastOrigin);
        }

        if (strtoupper($request->getMethod()) === 'OPTIONS') {
            $response = $response
                ->withHeader(
                    'Access-Control-Allow-Methods',
                    'POST, GET, PUT, PATCH, DELETE, OPTIONS'
                )
                ->withHeader('Access-Control-Allow-Headers', '*');
        }
    }

    return $response;
}

See also


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...