apitte
apitte copied to clipboard
Issues with Middleware/CORS
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:
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?
Hi @matronator, it's a complex bug report, thank you for that. Unfortunately I think your code is OK. I need from you to setup https://github.com/contributte/apitte-skeleton/ or pack your project (remove non-required stuff) and send it to me for further inspection. What's your choice?
ping @matronator
Hi @matronator, I've had simmilar issue, in my case no middleware were invoked during request. My problem was in www/index.php, where I used wrong application type Apitte\Core\Application\IApplication. I have changed it to Contributte\Middlewares\Application\IApplication and all middlewares started to work.