Skip to content

Issues with Middleware/CORS #212

@matronator

Description

@matronator

I've been banging my head on this for couple of hours now. I just can't seem to get CORS to work correctly. No matter what I do, no CORS headers are sent, no matter the method or URI.

Can someone please tell me what I'm missing?

config.neon (only the relevant stuff)

extensions:
    nettrine.annotations: Nettrine\Annotations\DI\AnnotationsExtension
    nettrine.cache: Nettrine\Cache\DI\CacheExtension
    middlewares: Contributte\Middlewares\DI\MiddlewaresExtension
    resource: Contributte\DI\Extension\ResourceExtension
    api: Apitte\Core\DI\ApiExtension

services:
    - App\Services\ParserService(%tplDir%)
    - App\Services\LoggerService
    # decorator.request.authentication:
    #     class: App\Api\Decorator\ExampleResponseDecorator
    #     tags: [apitte.core.decorator: [priority: 50]]
    middleware.tryCatch:
        factory: Contributte\Middlewares\TryCatchMiddleware
        tags: [middleware: [priority: 1]]
        setup:
            - setDebugMode(%debugMode%)
            - setCatchExceptions(%productionMode%) # used in debug only
            - setLogger(App\Services\LoggerService, 'info')
    middleware.logging:
        create: Contributte\Middlewares\LoggingMiddleware
        arguments: [App\Services\LoggerService]
        tags: [middleware: [priority: 100]]
    middleware.methodOverride:
        factory: Contributte\Middlewares\MethodOverrideMiddleware
        tags: [middleware: [priority: 150]]
    middleware.cors:
        factory: App\Api\Middleware\CORSMiddleware
        tags: [middleware: [priority: 200]]
    
    api.core.errorHandler: App\Model\ErrorHandler\ThrowingErrorHandler

api:
    debug: %debugMode%
    catchException: true
    plugins:
        Apitte\Core\DI\Plugin\CoreSchemaPlugin:
        Apitte\Core\DI\Plugin\CoreMappingPlugin:
            request:
                validator: Apitte\Core\Mapping\Validator\SymfonyValidator()
        Apitte\Core\DI\Plugin\CoreServicesPlugin:
        Apitte\Core\DI\Plugin\CoreDecoratorPlugin:
        Apitte\Debug\DI\DebugPlugin:
        Apitte\Middlewares\DI\MiddlewaresPlugin:
            tracy: true
            autobasepath: true
        Apitte\OpenApi\DI\OpenApiPlugin:
            swaggerUi:
                panel: %debugMode%
                url: null
                expansion: list # list|full|none
                filter: true # true|false|string
                title: Stacks Token Factory API

App/Api/Middleware/CORSMiddleware.php (I used the same as in apitte-skeleton, this version is only after trying to get it to work by trial and error)

<?php

declare(strict_types = 1);

namespace App\Api\Middleware;

use Contributte\Middlewares\IMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;

class CORSMiddleware implements IMiddleware
{
	private function decorate(ResponseInterface $response): ResponseInterface
	{
		return $response
			->withHeader('Access-Control-Allow-Origin', 'http://localhost:5173, http://localhost:3000, http://localhost:8000')
			->withHeader('Access-Control-Allow-Credentials', 'true')
			->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS, PATCH')
			->withHeader('Access-Control-Allow-Headers', '*');
	}

	/**
	 * Add CORS headers
	 */
	public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next): ResponseInterface
	{
		if ($request->getMethod() === 'OPTIONS') {
			return $this->decorate($response);
		}

		/** @var ResponseInterface $response */
		$response = $next($request, $this->decorate($response));

		return $this->decorate($response);
	}
}

Controller.php

#[Path('/convert/{type}')]
#[Method(['POST', 'OPTIONS'])]
#[RequestBody('Entity', Entity::class, true, true)]
#[RequestParameter('type', 'string')]
public function convert(ApiRequest $request, ApiResponse $response): ApiResponse
{
    if ($request->getMethod() === 'OPTIONS') {
        return $response
            ->withHeader('Access-Control-Allow-Origin', '*')
            ->withHeader('Access-Control-Allow-Credentials', 'true')
            ->withHeader('Access-Control-Allow-Methods', '*')
            ->withHeader('Access-Control-Allow-Headers', '*')
            ->withStatus(200);
        // exit;
    }

    /** @var Template $template */
    $template = $request->getEntity();
    $type = $request->getParameter('type');

    $content = $this->parserService->getTemplate($this->parserService->getFilePath($this->parserService->getFilenameFromType($type)));
    $parsed = $this->parserService->parse($content, (array) $template->arguments);

    $response = $response->writeBody($parsed)->withHeader('Content-Type', 'text/plain');

    return $response;
}

But I still get no additional headers added to my response and Chrome still complains:

Snímek obrazovky 2024-05-27 v 2 16 31

However what I don't understand is why Chrome is complaining about no CORS headers, when the preflight check passed apparently, because the only request showing in the console is a POST request and not OPTIONS.

Request:

POST /api/template/convert/token HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: cs-CZ,cs;q=0.9,en;q=0.8
Cache-Control: no-cache
Connection: keep-alive
Content-Length: 212
Content-Type: application/json
Host: localhost:8000
Origin: http://localhost:5173
Pragma: no-cache
Referer: http://localhost:5173/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-site
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36
sec-ch-ua: "Chromium";v="124", "Google Chrome";v="124", "Not-A.Brand";v="99"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "macOS"

Response:

HTTP/1.1 500 Internal Server Error
Host: localhost:8000
Date: Mon, 27 May 2024 00:09:13 GMT
Connection: close
X-Powered-By: Nette Framework 3
X-Frame-Options: SAMEORIGIN
Set-Cookie: _nss=1; path=/; HttpOnly; SameSite=Strict
Content-Type: text/html; charset=UTF-8

But then why is the response a 500 error, that on further inspection the code shouldn't even reach that far. The actual error is a missing property on the entity (tokenSupply), which is BS, because it is sent in the payload:

{"name":"yddd","editableUri":true,"userWallet":"SP39DTEJFPPWA3295HEE5NXYGMM7GJ8MA0TQX379","tokenName":"yddd","tokenSymbol":"WDD","tokenSupply":10000,"tokenDecimals":18,"tokenURI":"","mintable":false,"burnable":false}

I am thoroughly lost...

Also, WHY ARE THERE NO HEADERS IN THE RESPONSE when I am decorating everything in the CORSMiddleware?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions