前后端分离对接坑点记录
前言
现在很多项目都是采用前后端分离的模式来进行开发的,这样做有几个好处:
- 实现解耦,前后端分工明确,各司其职;
- 可以同时水平扩展前后端服务器;
- 减少后端服务器的并发/负载均衡压力;
- 提高开发效率
除了以上几点,前后端分离还有其他优势,这里就不细说了,但是前后端分离会导致跨域问题。跨域问题的根本原因:应为浏览器受到同源策略的限制,当前域名的js只能读取同域下的窗口属性。什么是同源策略?就是不同的域名,不同端口,不同的协议不允许共享资源,这是为了保障浏览器安全。同源策略是针对浏览器设置的门槛。
这里需要注意的是,只有访问类型为xhr(XMLHttpRequest)的才会出现跨域。
跨域问题的解决方案
- 修改浏览器的设置
- 修改请求的方式:jsonp
- CORS
jsonp是js前端发起请求的时候,利用只有xhr请求才会出现跨域的问题的特点,让服务端不在返回json数据,而是返回一段js代码,将json的数据以参数的形式传递到函数中,所以需要服务端配合。
把重点放在CORS。
CORS解决跨域
首先要知道什么是CORS。
跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。
比如,站点 http://domain-a.com 的某 HTML 页面通过 的 src 请求 http://domain-b.com/image.jpg。 网络上的许多页面都会加载来自不同域的CSS样式表,图像和脚本等资源。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。 例如,XMLHttpRequest和Fetch API遵循同源策略。 这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。
我们知道跨域访问的请求头中存在Origin的字段,用来记录当前的发起访问的域名,我们可以在服务端增加一个响应头Access-Control-Allow-Origin来告诉浏览器我们支持它的获取就可以了,这个可以在服务端代码里实现,也可以通过nginx反向代理实现,这里我推荐用nginx:
add_header Access-Control-Allow-Origin *;
*代表允许任何域名。
但是仅仅加这一句只能解决简单请求的跨域问题,简单请求满足一下几个条件:
- 使用下列方法之一
- GET
- HEAD
- POST
- Fetch 规范定义了对 CORS 安全的首部字段集合,不得人为设置该集合之外的其他首部字段。该集合为:
- Accept
- Accept-Language
- Content-Language
- Content-Type (需要注意额外的限制)
- DPR
- Downlink
- Save-Data
- Viewport-Width
- Width
- Content-Type 的值仅限于下列三者之一:
- text/plain
- multipart/form-data
- application/x-www-form-urlencoded
- 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;
- XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问。
- 请求中没有使用 ReadableStream 对象。
如果请求与简单请求不同,必须首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。”预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。
预检请求中会携带两个头部信息:
- Access-Control-Request-Method: POST
- Access-Control-Request-Headers: X-PINGOTHER, Content-Type
字段 Access-Control-Request-Method 告知服务器,实际请求将使用 POST 方法。字段 Access-Control-Request-Headers 告知服务器,实际请求将携带两个自定义请求首部字段:X-PINGOTHER 与 Content-Type。服务器据此决定,该实际请求是否被允许。
对此,我们需要对 OPTIONS 方法进行处理,并返回:
- Access-Control-Allow-Origin: http://foo.example
- Access-Control-Allow-Methods: POST, GET, OPTIONS
- Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
- Access-Control-Max-Age: 86400
首部字段 Access-Control-Allow-Methods 表明服务器允许客户端使用 POST, GET 和 OPTIONS 方法发起请求。
首部字段 Access-Control-Allow-Headers 表明服务器允许请求中携带字段 X-PINGOTHER 与 Content-Type。与 Access-Control-Allow-Methods 一样,Access-Control-Allow-Headers 的值为逗号分割的列表。
最后,首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。
如果我们的请求需要携带Cookie信息,响应头还需要 Access-Control-Allow-Credentials: true 否则浏览器将不会把相应内容返回个请求的发送者。
这里有个需要特别注意的时,如果需要携带Cookie,那么服务器不得设置 Access-Control-Allow-Origin 的值为“*”。 这是因为请求的首部中携带了 Cookie 信息,如果 Access-Control-Allow-Origin 的值为“*”,请求将会失败。而将 Access-Control-Allow-Origin 的值设置为 http://foo.example, 则请求将成功执行。
最后,给出我简单的nginx配置:
location /api {
add_header Access-Control-Allow-Headers Content-Type,Client-Type,Client-Ver,Client-Chan,Range;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Max-Age 86400;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS,DELETE;
if ($request_method = "OPTIONS") {
return 204;
}
proxy_pass http://backend;
}