Preskočiť na obsah

Príklad: CORS ​

API poznámok pre frontend na http://localhost:5173: GET a JSON POST na /api/v1/notes.

Otvoriť diagram v plnej veľkosti

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.

Vydané pod licenciou MIT.