CORS 发布请求失败
Posted
技术标签:
【中文标题】CORS 发布请求失败【英文标题】:CORS Post Request Fails 【发布时间】:2016-10-26 15:02:16 【问题描述】:我使用 SLIM 微框架构建了一个 API。我使用以下代码设置了一些添加 CORS 标头的中间件。
class Cors
public function __invoke(Request $request, Response $response, $next)
$response = $next($request, $response);
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, OPTIONS');
对于我的前端,我使用了 VueJS。我设置了 VueResource 并使用以下代码创建了一个函数。
register (context, email, password)
Vue.http(
url: 'api/auth/register',
method: 'POST',
data:
email: email,
password: password
).then(response =>
context.success = true
, response =>
context.response = response.data
context.error = true
)
在 chrome 中,控制台会记录以下错误。
XMLHttpRequest cannot load http://mysite:9800/api/auth/register. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://mysite' is therefore not allowed access.
奇怪的是,GET 请求运行良好。
【问题讨论】:
【参考方案1】:你在这里解决了一半的问题。
您缺少的是一个 OPTIONS 路由,其中还需要添加这些标头。
$app->options('/routes:.+', function ($request, $response, $args)
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, OPTIONS');
);
【讨论】:
谢谢!!!很好的答案,伙计。它现在完美运行。没有你我做不到。 @geggleto 据我所知,我发现对于 CORS,浏览器客户端有权发出 OPTIONS 的飞行前请求,并且它们会验证您上面所述的响应标头。这是否意味着禁用 OPTIONS 而不设置这些标头会导致失败?而且我们每次都必须return response.send(200);
来处理此类请求?【参考方案2】:
发生这种情况是因为预检请求属于 OPTIONS 类型。您需要为您的请求创建一个事件侦听器,它会检查类型并发送带有所需标头的响应。
不幸的是,我不知道 Slim 框架,但这是 Symfony 中的工作示例。
首先要返回的标题示例:
// Headers allowed to be returned.
const ALLOWED_HEADERS = ['Authorization', 'Origin', 'Content-Type', 'Content-Length', 'Accept'];
在请求监听器中,有一个 onKernelRequest 方法可以监视所有进来的请求:
/**
* @param GetResponseEvent $event
*/
public function onKernelRequest(GetResponseEvent $event)
// Don't do anything if it's not the master request
if (!$event->isMasterRequest())
return;
// Catch all pre-request events
if ($event->getRequest()->isMethod('OPTIONS'))
$router = $this->container->get('router');
$pathInfo = $event->getRequest()->getPathInfo();
$response = new Response();
$response->headers->set('Access-Control-Allow-Origin', $event->getRequest()->headers->get('Origin'));
$response->headers->set('Access-Control-Allow-Methods', $this->getAllowedMethods($router, $pathInfo));
$response->headers->set('Access-Control-Allow-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Expose-Headers', implode(', ', self::ALLOWED_HEADERS));
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Max-Age', 60 * 60 * 24);
$response->send();
这里我只是复制 Origin(所有域都可以请求资源,您应该将其更改为您的域)。 希望它能提供一些胶水。
【讨论】:
感谢您提供详细和彻底解释的答案,但此解决方案失败。【参考方案3】:实际上 CORS 是在浏览器级别实现的。即使有
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, OPTIONS');
chrome 和 Mozilla 不会设置标题以允许跨源。所以,你需要强行禁用它..
阅读有关禁用 CORS 的更多信息
Disable same origin policy in Chrome
【讨论】:
有关此答案的更多信息...为了处理飞行前要求,除了实际的 HTTP 动词之外,您还需要接受 OPTIONS。 想进一步解释吗? @geggleto 不过我想在生产中使用它 在生产中这将起作用,因为两者都在同一个域上。 slimframework.com/docs/cookbook/enable-cors.html 看看这里@JeromeCarter【参考方案4】:CORS 可能很难配置。关键是您需要在服务器和客户端中设置特殊标头,并且我没有看到任何 Vue 标头设置,除了据我所知 http
不是一个函数。但是,这里是发布请求的一些设置。
const data =
email: email,
password: password
const options =
headers:
'Access-Control-Expose-Headers': // all of your headers,
'Access-Control-Allow-Origin': '*'
Vue.http.post('api/auth/register', JSON.stringify(data), options).then(response =>
// success
, response =>
// error
)
请注意,您需要对数据进行字符串化,并且需要公开您的标头,通常包括 Access-Control-Allow-Origin
标头。
我在自己的一个应用程序中所做的是定义拦截器,因此我不必担心为每个请求设置标头。
Vue.http.headers.common['Access-Control-Expose-Headers'] = 'Origin, X-Requested-With, Content-Type, Accept, x-session-token, timeout, Content-Length, location, *'
Vue.http.headers.common['Access-Control-Allow-Origin'] = '*'
【讨论】:
以上是关于CORS 发布请求失败的主要内容,如果未能解决你的问题,请参考以下文章