CORS请求是预检的,但它似乎不应该是

Ray*_*lus 14 multipartform-data xmlhttprequest cors xmlhttprequest-level2

以下跨源POST请求,其内容类型为multipart/form-data且只有简单的标题是预检的.根据W3C规范,除非我读错了,否则不应该预先检查.我已经证实这种情况发生在Chrome 27和Firefox 10.8.3中.我没有测试任何其他浏览器.

以下是请求标头等:

Request URL:http://192.168.130.135:8081/upload/receiver
Request Method:POST
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Connection:keep-alive
Content-Length:27129
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryix5VzTyVtCMwcNv6
Host:192.168.130.135:8081
Origin:http://192.168.130.135:8080
Referer:http://192.168.130.135:8080/test/raytest-jquery.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.37 Safari/537.36
Run Code Online (Sandbox Code Playgroud)

这是OPTIONS(预检)请求:

Request URL:http://192.168.130.135:8081/upload/receiver
Request Method:OPTIONS
Status Code:200 OK
Request Headersview source
Accept:*/*
Accept-Encoding:gzip,deflate,sdch
Accept-Language:en-US,en;q=0.8
Access-Control-Request-Headers:origin, content-type
Access-Control-Request-Method:POST
Connection:keep-alive
Host:192.168.130.135:8081
Origin:http://192.168.130.135:8080
Referer:http://192.168.130.135:8080/test/raytest-jquery.html
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.37 Safari/537.36
Run Code Online (Sandbox Code Playgroud)

规范似乎很清楚:

更新:这是一些简单的客户端代码,它将重现这一点:

var xhr = new XMLHttpRequest(),
    formData = new FormData();

formData.append('myfile', someFileObj);

xhr.upload.progress = function(e) {
    //insert upload progress logic here
};

xhr.open('POST', 'http://192.168.130.135:8080/upload/receiver', true);
xhr.send(formData);
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么这是preflight?

Ray*_*lus 20

我最终检查了Webkit源代码,试图解决这个问题(谷歌没有产生任何有用的命中).事实证明,如果您注册onprogress事件处理程序,Webkit将强制任何跨源请求被预检.我不完全确定,即使在阅读了代码注释之后,为什么要应用这种逻辑.

在XMLHttpRequest.cpp中:

void XMLHttpRequest::createRequest(ExceptionCode& ec)
{
    ...

    options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;

    ...

    // The presence of upload event listeners forces us to use preflighting because POSTing to an URL that does not
    // permit cross origin requests should look exactly like POSTing to an URL that does not respond at all.
    // Also, only async requests support upload progress events.
    bool uploadEvents = false;
    if (m_async) {
        m_progressEventThrottle.dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
        if (m_requestEntityBody && m_upload) {
            uploadEvents = m_upload->hasEventListeners();
            m_upload->dispatchEvent(XMLHttpRequestProgressEvent::create(eventNames().loadstartEvent));
        }
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)


更新: Firefox显示与Webkit相同的逻辑.以下是nsXMLHttpRequest.cpp的相关代码:

nsresult
nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
{
    ...

    // Check if we need to do a preflight request.
    nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
    NS_ENSURE_TRUE(httpChannel, NS_ERROR_DOM_BAD_URI);

    nsAutoCString method;
    httpChannel->GetRequestMethod(method);
    if (!mCORSUnsafeHeaders.IsEmpty() ||
        (mUpload && mUpload->HasListeners()) ||
        (!method.LowerCaseEqualsLiteral("get") &&
         !method.LowerCaseEqualsLiteral("post") &&
         !method.LowerCaseEqualsLiteral("head"))) {
      mState |= XML_HTTP_REQUEST_NEED_AC_PREFLIGHT;
    }

    ...
}
Run Code Online (Sandbox Code Playgroud)

注意mUpload && mUpload->HasListeners()条件的部分.

似乎Webkit和Firefox(以及可能还有其他人)已经在他们的预检确定代码中插入了一些逻辑,而这些代码并未受到W3C规范的批准.如果我在规范中遗漏了某些内容,请发表评论.

  • 所以听起来这不是一个错误.在XHR规范中搜索"preflight"一词的第二个实例:http://www.w3.org/TR/XMLHttpRequest/这表明上传事件会强制执行预检,因为上传事件会引入新的无法获取的信息在CORS之前.然而令人困惑的是,这并未在CORS规范中捕获. (4认同)