我们只想做一个安分的软件“工程师”,只想专注于业务逻辑(因为老板只盯着他所关注的功能、版本、优化),不立志成为计算机“科学家”,解决那些底层的、不兼容、各种奇葩的实现(因为老板不觉得自己应该给这些“科研”工作出经费)。于是,从一开始,我们就本着“只要实现功能就行”的不科学态度,在基础知识上没有系统学习过……直到几次让人非常沮丧、非常被动的情况发生,我们才不得不审视一直以来的“短视”。
jsonp存在的问题
硬问题:
- 不支持POST方法
jsonp基于GET方法实现,并没有用XMLHttpRequest,不是真正的ajax请求。所以,仅有jsonp是不够的,服务端无论如何都免不了支持CORS。 - 欲捕获服务端返回的500等错误,捕获不到。jquery有文档明确说明:(function error) This handler is not called for cross-domain script and cross-domain JSONP requests. 我还不甚了了why
软问题:
服务端需要支持两种方式,而这种机制性的代码又不常改(除非出问题)。平时前端、后端都专注业务逻辑,前端没毛病,后端同学是万万不会想到这个代码。某一天,前端发现了问题,思量 “记得xx点原来是可以的啊,怎么今天不行了……”,又是一番google,一番尝试,结果发现:哦,原来是jsonp可以,json不行(要么就是json可以,jsonp不行)。这就是记忆的局限。毕竟,看不到对方代码,记忆中是支持的,却常常忽略了“两种方式”这件事。
注意:下面说的都是弃用jsonp后遇到的问题
ajax带不上cookie
这样子做:
1) Add the following to your ajax request.
xhrFields: { withCredentials:true }
2) Add the following to your response headers for resources in the different subdomain.
Access-Control-Allow-Origin : http://original.mydomain.com
Access-Control-Allow-Credentials : true
The XMLHttpRequest.withCredentials property is a Boolean that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates. Setting withCredentials has no effect on same-site requests.
IE8/IE9不支持ajax
我一直以为XMLHttpRequest是浏览器的标配,事实告诉我,真是没见过世面!人家伟大的IE8/IE9就不标配,有自己的XDomainRequest
幸好,有人为jquery写了插件jQuery-ajaxTransport-XDomainRequest。在引入jquery.js之后引入它,$.ajax的地方不用改,齐活。
强调一下,如果你引入了插件,发现仍然是返回500错误,请确认返回头中有Access-Control-Allow-Origin。
CORS requires the Access-Control-Allow-Origin header to be present in the AJAX response from the server.
我在这里费了好半天劲。因在我的印象中,我们一直是返回这个header的,否则在chrome、safari上早该有问题了。所以,在我读插件的文档时看到这句话,很自信地认为“恩,这步我们已经有了”。当我怀疑是插件没加载成、没执行、插件不靠谱等等各种尝试后,有点灰心的时候,不经意发现,返回的header中真的是没有Access-Control-Allow-Origin。为何呢?明明是服务端已加了的啊。
去查一下php代码,发现我们是从请求header中的Referer中取出http://domain.com:port/ 来生成Access-Control-Allow-Origin(没有Referer则没办法生成)。可现在IE的请求中没有Referer,倒是有个Origin。再去查查标准文档 w2c,才知道,原来Origin才是正确的姿势。
改了php代码,世界安静了。
后来,我终于在眼泪中明白,有些软件,总那么不可爱
然而,IE8/IE9问题只是“貌似”解决了。当时应该也是没有全面测试,后来发现,登录不了。原来是带不上Cookie!也就是说,XDomainRequest实现的有局限。参见:微软某程序员的一篇blog。
看来,如果需要兼容IE8,是不能完美实现CORS的。
最终的最终,我们还是无奈地选择了“js中访问同域名下的php,nginx做代理(将请求转发给api)”这种方式。
refs:
http://stackoverflow.com/questions/2870371/why-is-jquerys-ajax-method-not-sending-my-session-cookie
http://www.w3.org/TR/cors/
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
http://bob.ippoli.to/archives/2005/12/05/remote-json-jsonp/
https://en.wikipedia.org/wiki/JSONP