Angular4.x项目如何实现跨域

跨域

跨域,指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对 javascript 施加的安全限制。

浏览器的同源策略会导致跨域,这里同源策略又分为以下两种:

  1. DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
  2. XmlHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

什么情况下跨域了?
只要协议、域名、端口有任何一个不同,都被当作是不同的域,之间的请求就是跨域操作。举个例子:
http://www.example.com(:80)/,调用http://www.example.com:8080/(端口号不同)
http://www.example.com(:80)/,调用http://api.example.com/(子域名不同)
http://www.example.com(:80)/,调用https://www.example.com/(协议不同)

上面三种都存在跨域问题,想必有些人肯定会困惑,浏览器为什么会有跨域的限制呢?有经验的人肯定知道,跨域限制主要是为了安全考虑。

解决方式

JSONP

基本原理就是通过动态创建 script 标签,因为 script 没有跨域限制。使用方法就不赘述了,但是要注意 JSONP 只支持 GET 请求,不支持 POST 请求。

CORS

实现 CORS 通信的关键是服务器,因为不是太懂服务端,这里只说关键设置,需要在 response header 中设置如下两个字段:

1
2
header('Access-Control-Allow-Origin: *'); //允许所有来源访问,或者换成指定地址
header('Access-Control-Allow-Credentials: true')

我在 nginx 上这样设置的:

1
2
3
4
5
6
7
8
location / {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
index index.html index.htm;
root www;
try_files $uri $uri/ /index.html =404;
}

具体教程参见:跨域资源共享CORS详解
Cors在nginx上的设置:CORS on Nginx

代理

这里分为两种环境:开发环境和生产部署环境。

本地测试一般都属于开发环境,如果用了Angular-cli搭建项目,跨域就简单了,只需两步:

  1. 在项目根目录新建一个文件proxy.conf.json,添加如下内容

    1
    2
    3
    4
    5
    6
    7
    {
    "/v2": {
    "target": "https://api.douban.com",
    "secure": false,
    "changeOrigin": true //记得加这句
    }
    }
  2. 修改同目录下的package.json的 scripts 内容

    1
    2
    3
    4
    5
    {
    "scripts": {
    "start": "ng serve --proxy-config proxy.conf.json --open"
    }
    }

然后向这个 url 发起 get 请求/v2/movie/top250?count=10,这样就能轻松实现跨域了。

17_11_6_01.png

但是项目要部署了怎么办?上面这个方法还管用吗?答案是当然不能,因为有些设置没有生效到服务器。

浏览器有跨域限制,但是服务器不存在跨域问题,所以可以由服务器请求所要域的资源再返回给客户端。

这里不得不说说 nginx 的反向代理,利用 nginx 反向代理实现跨域,是最简单的跨域方式,支持所有浏览器,并且不会影响服务器性能。这是一种对于任何项目都通用的方法!nginx下载链接

下面是我写的一个简单的 location 匹配规则。

1
2
3
4
location ^~ /v2/movie/ {
rewrite ^/v2/movie/(.*)$ /v2/movie/$1 break;
proxy_pass https://api.douban.com;
}

假设前端请求 URL 为:/v2/movie/top250?count=10,location会匹配以/v2/movie/为开头的地址,rewrite 重写 url,$1引用的是括号里的内容(.*),last 一般写在 server 和 if 中,而 break 一般使用在 location 中。proxy_pass 转发请求到真实服务器,其实真实的请求url为:https://api.douban.com/v2/movie/top250?count=10

17_11_6_02.png

常用正则

. : 匹配除换行符以外的任意字符
? : 重复0次或1次
+ : 重复1次或更多次
* : 重复0次或更多次
\d : 匹配数字
^ : 匹配字符串的开始
$ : 匹配字符串的结束
{n} : 重复n次
{n,} : 重复n次或更多次
[c] : 匹配单个字符c
[a-z] : 匹配a-z小写字母的任意一个

更多正则基础请点这里

总结

  • 如果你要请求的是其他公司开发提供的接口,例如上面的豆瓣api,那么 jsonpnginx代理 是不错的方案;

  • 要是自己公司开发的接口,例如:http(s)://api.example.com/*,那么 CORSnginx代理 是最合适的方案,一般的项目采用前者,大型网站可能要做负载均衡,Apache 结合 nginx 一起用;

  • 其实项目中这三种方法可以结合使用,从而可以发挥出更多的功能。