Príklad: CORS
API poznámok pre frontend na http://localhost:5173: GET a JSON POST na /api/v1/notes.
Viac originov
Príklad používa jeden pevný origin, ktorý funguje v 3.5.0 aj neskôr. Pre viac originov pribudlo vo verzii 3.6.0 setCorsAllowedOrigins(). Pozrite si CORS.
Služby
yaml
application:
mapping:
Api: Tomaj\NetteApi\Presenters\*Presenter
parameters:
# where the browser frontend runs
frontendOrigin: 'http://localhost:5173'
services:
router: App\RouterFactory::create
- Tomaj\NetteApi\Output\Configurator\EnvConfigurator
- Tomaj\NetteApi\Error\DefaultErrorHandler
apiPresenter:
factory: Tomaj\NetteApi\Presenters\ApiPresenter
setup:
# sent as Access-Control-Allow-Origin on every response.
# Other values: '*' (the default), 'auto' (the origin of the Referer header,
# with Access-Control-Allow-Credentials: true) and 'off' (no header)
- setCorsHeader(%frontendOrigin%)
apiDecider:
factory: Tomaj\NetteApi\ApiDecider
setup:
- addApi(Tomaj\NetteApi\EndpointIdentifier('GET', 1, 'notes'), App\Api\NoteListHandler(), Tomaj\NetteApi\Authorization\NoAuthorization())
- addApi(Tomaj\NetteApi\EndpointIdentifier('POST', 1, 'notes'), App\Api\NoteCreateHandler(), Tomaj\NetteApi\Authorization\NoAuthorization())
# answer OPTIONS for every registered endpoint, so the browser may send
# the Content-Type and Authorization headers
- enableGlobalPreflight(
Tomaj\NetteApi\Handlers\CorsPreflightHandler(headers: [
Access-Control-Allow-Headers: [Content-Type, Authorization],
Access-Control-Max-Age: '600'
])
)Handler
php
<?php
declare(strict_types=1);
namespace App\Api;
use Tomaj\NetteApi\Handlers\BaseHandler;
use Tomaj\NetteApi\Params\JsonInputParam;
use Tomaj\NetteApi\Response\JsonApiResponse;
use Tomaj\NetteApi\Response\ResponseInterface;
final class NoteCreateHandler extends BaseHandler
{
public function params(): array
{
return [
(new JsonInputParam('note', '{"type": "object", "properties": {"text": {"type": "string"}}, "required": ["text"]}'))
->setRequired(),
];
}
public function handle(array $params): ResponseInterface
{
return new JsonApiResponse(201, ['note' => ['id' => 2, 'text' => $params['note']['text']]]);
}
}Test
bash
#!/usr/bin/env bash
# End-to-end check: the CORS headers a browser needs for a cross-origin GET and a JSON POST.
set -euo pipefail
cd "$(dirname "$0")"
[[ -f vendor/autoload.php ]] || { echo "fail: vendor/ is missing, run composer install first" >&2; exit 1; }
source ../ci/lib.sh
serve 8084
request -H 'Origin: http://localhost:5173' "$BASE/api/v1/notes"
expect_status 200 "GET"
expect_header 'Access-Control-Allow-Origin: http://localhost:5173' "GET"
expect_header 'Access-Control-Allow-Methods: POST, DELETE, PUT, GET, OPTIONS' "GET"
ok "GET carries Access-Control-Allow-Origin for the frontend"
request -X OPTIONS -H 'Origin: http://localhost:5173' -H 'Access-Control-Request-Method: POST' \
-H 'Access-Control-Request-Headers: content-type' "$BASE/api/v1/notes"
expect_status 200 "preflight"
expect_header 'Access-Control-Allow-Headers: Content-Type' "preflight"
expect_header 'Access-Control-Allow-Headers: Authorization' "preflight"
expect_header 'Access-Control-Max-Age: 600' "preflight"
ok "OPTIONS is answered by the global preflight handler"
request -X POST -H 'Origin: http://localhost:5173' -H 'Content-Type: application/json' -d '{"text":"Call mom"}' "$BASE/api/v1/notes"
expect_status 201 "POST"
expect_header 'Access-Control-Allow-Origin: http://localhost:5173' "POST"
ok "the JSON POST after the preflight succeeds"
request -X OPTIONS "$BASE/api/v1/unknown"
expect_status 404 "preflight for an unknown endpoint"
ok "OPTIONS for an endpoint that does not exist answers 404"Časté otázky
Prečo frontend potrebuje preflight?
POST s Content-Type: application/json nie je jednoduchá požiadavka, takže prehliadač sa najprv opýta cez OPTIONS.
Prečo je Content-Type v Allow-Headers?
Predvolené preflight hlavičky povoľujú len Authorization a X-Requested-With; JSON telo potrebuje Content-Type.
Čo robí Max-Age 600?
Prehliadač si odpoveď na preflight pamätá 10 minút.
Kde nastavím origin pre jednotlivé prostredia?
frontendOrigin je NEON parameter; prepíšte ho v lokálnej alebo produkčnej konfigurácii.
Funguje OPTIONS na neznámu URL?
Nie, vráti 404, ako ukazuje test. Pozrite si CORS.