最近,我们对跨域请求有需求,于是我今天下午和晚上花了大概四个小时左右调通了前端和后端。在整个过程中,大部分时间都花在找资料上,深感网上的资料太零散,比较难找,所以整理一番,分享给大家。
在正文之前,如果你对CORS不甚了解,请先阅读阮一峰老师的跨域资源共享 CORS 详解,文章非常的透彻全面。(汗!我google出来看的第一篇文章是另外一篇,整篇文章看了之后,知道的大概,但是完全不知道如何下手。后来是多番查找代码,各种试错,基本上搞定的时候,才看到阮一峰老师的这篇文章)。
好,咱们正文开始!
一、服务器配置
环境
语言:php
框架:CI + codeigniter-restserver
CROS请求分成简单请求和非简单请求。而我们在整个前后台通信中,数据交互格式是JSON的,而且存在许多除HEAD、GET、POST之外的请求,所以我们的请求都是非简单请求
。而且,我们在CI中是通过session验证权限的,所以需要传递cookie
。
针对上面这两点要求,我们有以下配置:
1、对普通的GET/POST/PUT请求,请求头设置如下:
1 2 3 4 5 6 7 8
| //设置json格式请求头 header("Content-type:application/json; charset=utf-8"); //跨域请求允许的域名设置,因为需要传递cookie,不能使用* header("Access-Control-Allow-Origin: http://www.beyondwinlaw.com"); //跨域请求允许的请求头 header("Access-Control-Allow-Headers: Content-type"); //跨域请求同意发送Cookie header("Access-Control-Allow-Credentials: true");
|
2、 非简单请求每次请求前,都会发送一个一次”预检“请求,它是options
的请求方式。它主要是询问服务器是否允许这个非简单请求访问,如果我们允许,则返回所需要的回应头信息(response header),这个预检请求的请求头设置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13
| //设置json格式请求头 header("Content-type:application/json; charset=utf-8"); //跨域请求允许的域名设置 header("Access-Control-Allow-Origin: http://www.beyondwinlaw.com"); //跨域请求允许的请求头 header("Access-Control-Allow-Headers: Content-type"); header("Vary: Accept-Encoding, Origin"); //跨域请求同意发送Cookie header("Access-Control-Allow-Credentials: true"); //options请求中所允许的方法 header("Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS"); //OPTIONS这个预请求的有效时间,20天 header("Access-Control-Max-Age: 1728000");
|
在配置上面之前,我们还需要对预检请求做出回应。而在[codeigniter-restserver(https://github.com/chriskacerguis/codeigniter-restserver)框架中,使用的是RESTful API的风格,get_name
的options
请求方法是下面这么写的:
1 2
| public funtion get_name_options() { }
|
当然,我们不可能对所有的方法都写一个对应的options
请求方法,那如何做呢?我们是在early_checks()
做这件事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| /** * 请求之前检查,每个接口都会处理 */ public function early_checks() { parent::early_checks(); // TODO: Change the autogenerated stub /** * 将get或post请求中的参数为空/两边有空格的数据处理一下 */ switch ($this->request->method) { case "get": { $this->_get_args = $this->handle_param($this->_get_args); } break; case "post": { $this->_post_args = $this->handle_param($this->_post_args); } break; case "options": { //options请求,直接成功, 它设置了允许的方法 header("Access-Control-Allow-Methods: GET, POST, PUT, OPTIONS"); //OPTIONS这个预请求的有效时间,20天 header("Access-Control-Max-Age: 1728000"); //做出回应 answer(true, '成功'); } break; } }
|
注:answer方法中是统一的回应方法,内部已经设置了统一的请求头。
二、Angular中的配置
Angular中的配置更简单了,只需要对$http的GET/POST/PUT
请求中的配置项中,将withCredentials
设置为true就行了。
请求格式如下:
1 2 3
| $http.get(url, [config]).success(function(){ ... }); $http.post(url, param, [config]).success(function(){ ... }); $http.put(url, param, [config]).success(function(){ ... });
|
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| //GET请求 $http.get('/test?name=Jack', { withCredentials: true }).success(function (data) { console.log(data); }).error(function (data) { console.log(data); }); //POST请求 $http.post('/test', {name: 'Jack'}, { withCredentials: true }).success(function (data) { console.log(data); }).error(function (data) { console.log(data); }); //PUT请求 $http.put('/test', {name: 'Luck'}, { withCredentials: true }).success(function (data) { console.log(data); }).error(function (data) { console.log(data); });
|
还需要提一下的是,我们在使用ng-file-upload
进行上传时,设置withCredentials为true可以如下:
1 2 3 4 5 6 7 8 9 10 11
| Upload.upload({ url: ServerURL + 'file/file_upload', data: { file: file }, withCredentials : true }).progress(function (evt) { // 进度 console.log('进度: ' + parseInt(100.0 * evt.loaded / evt.total) + '%'); }).success(function(data, status, headers, config) { console.log(data); }).error(function(data) { console.log(data); });
|
总结
虽然折腾了很久,其实真正知道了,配置起来特别的简单。所以能找到一篇好的文章,是真的很幸福的一件事情。
参考
跨域资源共享 CORS 详解
陈斌彬的技术博客
HTTP访问控制(CORS)
Angular通过CORS实现跨域方案
withCredentials is not working