jQuery CORS 请求因 FireFox 中的预检选项请求而终止

标签 jquery clojure cors embedded-jetty compojure

我无法判断故障发生在哪里,这是 jQuery 问题还是 Compojure 问题还是什么。

我想发出这个跨域请求:

function signup() {

var signup_username = $('#signup_username').val(); 
var signup_password_1 = $('#signup_password_1').val(); 
var signup_password_2 = $('#signup_password_2').val(); 

$.ajax({
    type: "POST",
    contentType: "application/json",
    url: "http://localhost:40000/signup",
data: 
    JSON.stringify({
        "signup_username": signup_username,
        "signup_password_1": signup_password_1,
        "signup_password_2": signup_password_2
    }),

    complete: function (data) { console.log(data); alert("Done. Look at the console.log to see the results.");  },
    success: function (data) { console.log(data);  },
    error:  function (data) { console.log(data); },
    dataType: "json"
});
}

我在服务器上编写了一个小型 Clojure 应用程序,使用嵌入式 Jetty 作为服务器。我这样定义我的 Compojure 路由:

(defroutes app-routes
  (GET "/" request (login-form request))
  (POST "/" request (login request))
  (OPTIONS "/" request (preflight request))
  (GET "/signup" request (signup-form request))
  (POST "/signup" request (signup request))
  (OPTIONS "/signup" request (preflight request))
  (route/resources "/")
  (route/not-found "Page not found. Check the http verb that you used (GET, POST, PUT, DELETE) and make sure you put a collection name in the URL, and possibly also a document ID."))

我正在本地计算机上进行测试,但我想测试跨域,因此我在 2 个不同的端口上启动同一个应用程序:34001 和 40000。我将 FireFox 指向 34001,然后 Javascript 应该进行交叉- 域调用 40000。

首先,我使用 CURL 进行测试:

curl -X OPTIONS --verbose  http://localhost:40000/signup

给我:

* About to connect() to localhost port 40000 (#0)
*   Trying ::1... connected
* Connected to localhost (::1) port 40000 (#0)
> OPTIONS /signup HTTP/1.1
> User-Agent: curl/7.21.4 (universal-apple-darwin11.0) libcurl/7.21.4 OpenSSL/0.9.8x zlib/1.2.5
> Host: localhost:40000
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Sun, 13 Jul 2014 20:43:43 GMT
< Access-Control-Allow-Origin: localhost:40000
< Access-Control-Allow-Methods: PUT, DELETE, POST, GET, OPTIONS, XMODIFY
< Access-Control-Max-Age: 2520
< Access-Control-Allow-Credentials: true
< Access-Control-Request-Headers: x-requested-with, content-type, origin, accept
< Access-Control-Allow-Headers: x-requested-with, content-type, origin, accept
< Content-Type: application/json;charset=ISO-8859-1
< Content-Length: 12
< Server: Jetty(7.x.y-SNAPSHOT)
< 
* Connection #0 to host localhost left intact
* Closing connection #0

所以我看到了我期望看到的大部分标题。

现在我使用 FireFox 进行测试,并启用 Firebug。 Firebug 没有显示任何正在发出的预检 OPTIONS 请求,而是我只看到错误:

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:40000/signup. This can be fixed by moving the resource to the same domain or enabling CORS.

奇怪。我想知道 FireFox 是否正在发出预检选项请求。所以我启动了 Charles http://www.charlesproxy.com/

Charles 向我展示了 FireBug 没有的事件。查尔斯向我展示了这个请求:

OPTIONS /signup HTTP/1.1
Host: localhost:40000
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:30.0) Gecko/20100101 Firefox/30.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: http://localhost:34001
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

查尔斯向我展示了这个回复:

HTTP/1.1 200 OK
Date: Sun, 13 Jul 2014 20:35:27 GMT
Access-Control-Allow-Origin: http://localhost:34001
Access-Control-Allow-Methods: DELETE, GET, POST, PUT
Content-Type: application/octet-stream;charset=ISO-8859-1
Content-Length: 18
Server: Jetty(7.x.y-SNAPSHOT)

preflight complete

至少我面临的问题之一是,这个响应来自哪里?正如您从上面的 Compojure 路由中看到的,该请求应该已发送到“预检”函数,我的定义如下:

(defn preflight [request]
  "2014-07-13 - this is meant to enable CORS so our frontenders can do cross-browser requests. The browser should do a 'preflight' OPTIONS request to get permission to do other requests."
  (print " IN PREFLIGHT ")
  (println " headers host is: " (str (get-in request [:headers "host"])))
  (assoc
      (ring.util.response/response "CORS enabled")
    :headers {"Content-Type" "application/json"
              "Access-Control-Allow-Origin" (str (get-in request [:headers "host"]))
              "Access-Control-Allow-Methods" "PUT, DELETE, POST, GET, OPTIONS, XMODIFY" 
              "Access-Control-Max-Age" "2520"
              "Access-Control-Allow-Credentials" "true"
              "Access-Control-Request-Headers" "x-requested-with, content-type, origin, accept"
              "Access-Control-Allow-Headers" "x-requested-with, content-type, origin, accept"}))

当我使用 CURL 测试时可以看到这些 header ,但当我使用 FireFox 时却看不到。当我使用 CURL 而不是 FireFox 时,我的 printlin 语句也会打印到终端。

出于某种原因,来自 FireFox 的预检请求不会进入我的预检功能。事实上,我不知道它去了哪里。它得到的响应与我定义的任何路由都不匹配。

有人知道发生了什么事吗?

我注意到这个家伙尽了最大努力并认为 CORS 无法使用:

http://chstrongjavablog.blogspot.com/2013/04/enabling-cors-for-jetty.html

更新:

这看起来很可疑:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

怎么说“application/json”?

如何控制 FireFox 发出的预检请求?还是 jQuery 可以做到这一点?

此外,这个回应看起来非常有限:

Content-Type: application/octet-stream;charset=ISO-8859-1

同样,我无法弄清楚是什么导致了这种返回,因此我对响应没有太多控制权。

当我在 Chrome 中测试此功能时,出现以下错误:

 XMLHttpRequest cannot load http://localhost:40000/signup. Request header field Content-Type is not allowed by Access-Control-Allow-Headers. 

而且,当我使用 CURL 进行测试时,我得到了正确的 header ,但不知何故 Chrome 和 FireFox 会转到我从未定义过的路线。

如果我这样做:

curl -X OPTIONS --verbose  http://localhost:40000/signup

我得到这些响应标题:

< HTTP/1.1 200 OK
< Date: Sun, 13 Jul 2014 22:45:00 GMT
< Access-Control-Allow-Origin: localhost:40000
< Access-Control-Allow-Methods: PUT, DELETE, POST, GET, OPTIONS, XMODIFY
< Access-Control-Max-Age: 2520
< Access-Control-Allow-Credentials: true
< Access-Control-Allow-Headers: x-requested-with, content-type, origin, accept
< Content-Type: application/json;charset=ISO-8859-1
< Content-Length: 12
< Server: Jetty(7.x.y-SNAPSHOT)

其中包括具有“内容类型”的 Access-Control-Allow-Headers。但在 Chrome 和 FireFox 中,浏览器都会执行预检 OPTIONS 调用,该调用获得的响应似乎并非来 self 定义的任何路由。

如果我使用 Charles 网络调试器,我会看到 FireFox 发送这些 header :

OPTIONS /signup HTTP/1.1
Host: localhost:40000
Access-Control-Request-Method: POST
Origin: http://localhost:34001
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://localhost:34001/signup
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8

并获得此响应:

HTTP/1.1 200 OK
Date: Sun, 13 Jul 2014 22:42:58 GMT
Access-Control-Allow-Origin: http://localhost:34001
Access-Control-Allow-Methods: DELETE, GET, POST, PUT
Content-Type: application/octet-stream;charset=ISO-8859-1
Content-Length: 18
Server: Jetty(7.x.y-SNAPSHOT)

preflight complete

我不知道这个回应来自哪里。如果我对整个项目都这样做:

grep -iR "preflight complete" *

短语“预检完成”出现在我的代码所在的位置。那么这个响应来自哪里呢?为什么 FireFox 和 Chrome 的最终路径与我在 CURL 中进行调用时的路径不同?

更新:我终于找到了答案。

我折腾了几个小时,试图让 CORS 工作,在许多其他实验中,我应用了这个中间件:

https://github.com/r0man/ring-cors

这覆盖了我发回的 header 。我很好奇为什么这种覆盖只发生在 Ajax 请求而不是curl 请求上。也许这是放入ring-cors 中的一些神奇的r0man。不管怎样,我删除了它,现在我看到了我发回的 header 。

最佳答案

您将必须处理两个请求,第一个是选项,第二个是帖子。幸运的是,有人编写了一个框架来使这变得简单( https://github.com/r0man/ring-cors )。

如果您决定使用ring-cors,那么您可以通过以下方式包装所有路由:

(def app 
  (-> (handler/api app-routes)
      (wrap-cors :access-control-allow-origin #"yoursite"
                 :access-control-allow-methods [:get :put :post]
                 :access-control-allow-headers ["Content-Type"])))

关于jQuery CORS 请求因 FireFox 中的预检选项请求而终止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24727005/

相关文章:

javascript - 从 bootstrap datetimepicker 获取日期和时间

滚动条上的 Jquery 径向动画条

jquery - 在哪里可以找到 jQuery 颜色插件的缩小版?

Clojure:特殊形式、函数和宏的实现方式之间的差异

vector - 根据索引集合获取数组值

javascript - Jquery水平滚动错误

Clojure join 无法根据过滤函数的结果创建字符串

java - Postman 的 GET 的 Cors 不显示 header

c# - WebAPI - 为子域启用 CORS

asp.net-web-api - 协商后跨源SignalR连接停止