HTML5中提供了跨域加载数据的方法,让我们得以从JSONP或者Flash中介等各种绕行方案中解脱出来,更加顺畅地与服务器交流。另一方面,因为PHP是最好的语言……所以在它与Node.js之间,我选择前者作为后端语言开发内容服务。
这篇文章记录使用jQuery+PHP开发跨域应用时的小心得。
身份验证
做身份验证,最简单的办法就是使用PHP的SESSION保存用户信息,于是就要用到Cookie。默认情况下,跨域Ajax请求发起时候不包含Cookie,需要我们主动将XHR
的withCredentials
属性设为true
才行。
jQuery会把XHR
封装成jqXHR
,并且不暴露真正的XHR
(说实话这点有点难以理解,尤其是在做上传进度条的时候)。然后它提供一个给真正XHR
赋值的接口xhrField
,所以写成代码就是这样事儿的:
$.ajax(url, {
xhrField: {
withCredentials: true
}
}
各种HTTP头
如果不需要验证用户身份,直接在HTTP头中输出Access-Control-Allow-Origin: *
即可。
我的产品需要验证,那么首先,HTTP头中必须有Access-Control-Allow-Credentials: true
;此时对域的限制也严格许多,不再允许像前面那样使用*
放开给任何来源,必须指明哪个具体域可以接受。
关于Access-Control-Allow-Origin
的值,规范中的说明是“域名列表或null”,然则接下来的“注意”有点诡异:“实际生产中,‘列表或null’要求更严格。你可以认为它实际只允许单一域名或null,而非空格分隔的域名列表。”——既然如此你干脆写个“域名或null”不就完了……
总之对于我们而言,返回的HTTP头中还要包含Access-Control-Allow-Origin: http://域名
,指定允许作为来源的协议、域名、端口,并且只能有一个(组)。因为通常来说我们开发环境和生产环境不一样,所以这里的域名最好不要写在服务器配置里;使用PHP,通过$_SERVER['HTTP_ORIGIN']
取出访问来源,与白名单比对,通过后再输出相应的头,更加合适。
调试
我选择JSON作为前后端交流的格式。为了方便浏览器解析(也是HTML5的要求),我还返回了Content-type: application/json
头。
使用PHP少不了使用Xdebug。出现错误时,Xdebug会返回完整的栈,有利排查。但是为了方便阅读,Xdebug还会给返回信息套上<table>
结构,这时Chrome的Network工具就会把它解析成奇怪的格式,所以Content-type
一定要最后和数据一起返回。
与之相反的是前文说到的Access-Control-Allow-Origin
和Access-Control-Allow-Credentials
,这二位必须放在最前面。不然如果出现500
错,响应头不包含这两个跨域标记,Chrome就会理所当然地不显示返回内容,也就无法看到错误描述,根本无法排查。
欢迎吐槽,共同进步