Skip to content

设置CORS

CORS - 跨域资源共享

在实现 CORS 支持时以下的流程图可以提供一个很好的参考:

CORS服务器流程图

你可以在这里测试你的 CORS 支持:http://www.test-cors.org/

你可以在这里阅读规范:https://www.w3.org/TR/cors/

简单解决方案

对于简单的 CORS 请求,服务器只需在响应头中添加以下标头:

Access-Control-Allow-Origin: <domain>, ... 

以下代码将启用懒惰的CORS。

$app->options('/{routes:.+}', function ($request, $response, $args) {
    return $response;
});

$app->add(function ($request, $handler) {
    $response = $handler->handle($request);
    return $response
            ->withHeader('Access-Control-Allow-Origin', 'http://mysite')
            ->withHeader('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type, Accept, Origin, Authorization')
            ->withHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
});

将以下路由作为最后一个路由添加:

<?php
use Slim\Exception\HttpNotFoundException;

/**
 * 捕获不匹配任何路由的404 Not Found页面的路由
 * 注意:确保此路由定义在最后
 */
$app->map(['GET', 'POST', 'PUT', 'DELETE', 'PATCH'], '/{routes:.+}', function ($request, $response) {
    throw new HttpNotFoundException($request);
});

Access-Control-Allow-Methods

以下中间件可以用于查询 Slim 的路由器,并获取特定模式实现的方法列表。

下面是一个完整的示例应用程序:

<?php

use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Factory\AppFactory;
use Slim\Routing\RouteCollectorProxy;
use Slim\Routing\RouteContext;

require_once __DIR__ . '/../vendor/autoload.php';

$app = AppFactory::create();

$app->addBodyParsingMiddleware();

// 这个中间件将使用允许的所有方法附加响应头Access-Control-Allow-Methods
$app->add(function (Request $request, RequestHandlerInterface $handler): Response {
    $routeContext = RouteContext::fromRequest($request);
    $routingResults = $routeContext->getRoutingResults();
    $methods = $routingResults->getAllowedMethods();
    $requestHeaders = $request->getHeaderLine('Access-Control-Request-Headers');

    $response = $handler->handle($request);

    $response = $response->withHeader('Access-Control-Allow-Origin', '*');
    $response = $response->withHeader('Access-Control-Allow-Methods', implode(',', $methods));
    $response = $response->withHeader('Access-Control-Allow-Headers', $requestHeaders);

    // 可选项:允许带有Authorization头的Ajax CORS请求
    // $response = $response->withHeader('Access-Control-Allow-Credentials', 'true');

    return $response;
});

// The RoutingMiddleware should be added after our CORS middleware so routing is performed first
$app->addRoutingMiddleware();

// The routes
$app->get('/api/v0/users', function (Request $request, Response $response): Response {
    $response->getBody()->write('List all users');

    return $response;
});

$app->get('/api/v0/users/{id}', function (Request $request, Response $response, array $arguments): Response {
    $userId = (int)$arguments['id'];
    $response->getBody()->write(sprintf('Get user: %s', $userId));

    return $response;
});

$app->post('/api/v0/users', function (Request $request, Response $response): Response {
    // 获取JSON数据
    $parameters = (array)$request->getParsedBody();

    $response->getBody()->write('Create user');

    return $response;
});

$app->delete('/api/v0/users/{id}', function (Request $request, Response $response, array $arguments): Response {
    $userId = (int)$arguments['id'];
    $response->getBody()->write(sprintf('Delete user: %s', $userId));

    return $response;
});

// 允许预检请求
// 由于浏览器在发送请求时的行为,
// 你必须添加OPTIONS方法。了解有关预检的详细信息。
$app->options('/api/v0/users', function (Request $request, Response $response): Response {
    // Do nothing here. Just return the response.
    return $response;
});

// Allow additional preflight requests
$app->options('/api/v0/users/{id}', function (Request $request, Response $response): Response {
    return $response;
});

// 使用组
$app->group('/api/v0/users/{id:[0-9]+}', function (RouteCollectorProxy $group) {
    $group->put('', function (Request $request, Response $response, array $arguments): Response {
        // 在这里写你的代码...
        $userId = (int)$arguments['id'];
        $response->getBody()->write(sprintf('Put user: %s', $userId));

        return $response;
    });

    $group->patch('', function (Request $request, Response $response, array $arguments): Response {
        $userId = (int)$arguments['id'];
        $response->getBody()->write(sprintf('Patch user: %s', $userId));

        return $response;
    });

    // 允许预检请求
    $group->options('', function (Request $request, Response $response): Response {
        return $response;
    });
});

$app->run();

Access-Control-Allow-Credentials

如果请求包含凭证(cookie、授权头部或 TLS 客户端证书),则可能需要将 Access-Control-Allow-Credentials 头部添加到响应对象中。

$response = $response->withHeader('Access-Control-Allow-Credentials', 'true');