我无法分辨故障发生的位置,这是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."))
我正在我的本地机器上测试,但我想测试跨域,所以我在两个不同的端口上启动相同的应用程序: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是否正在进行预检OPTIONS请求。所以我启动了查尔斯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测试时,我可以看到这些标题,但是当我使用FireFox时却看不到。当我使用CURL时,我的printlin语句也会打印到终端,而不是FireFox。
出于某种原因,来自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进行测试时,我得到了正确的标题,然而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调用,该调用获得的响应似乎不是来自我定义的任何路由。
如果我使用Charles网络调试器,我会看到FireFox发送这些标头:
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
并且超出了我发回的标头。我很好奇为什么这个覆盖只发生在Ajax请求而不是curl请求。也许这是一些神奇的r0man投入戒指。无论哪种方式,我删除它,现在我看到我发回的标题。
答案 0 :(得分:4)
您将不得不处理两个请求,第一个是选项,第二个是帖子。幸运的是,有人编写了一个框架来简化这一过程(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"])))