跨域请求中的cookies处理

现在的前后端开发已经完全分离,后端服务器和前端服务器分别部署在不同的服务器。同时也对应不同的域名,所以跨域请求领域方面的知识也需要补充。

# 跨域请求添加 header

我们都知道,出于安全考虑,JS 是有同源策略限制。所以,我们在对其他域名发起请求的时候需要添加 http header。

Access-Control-Allow-Origin: *
1

这个参数的值只能为星号或者具体的网址,星号代表所有网站。

JS 跨域请求有两个 API 可以使用,XMLHttpRequestfetchXMLHttpRequest会默认带上 cookies,但是fetch默认不会带上。如果需要带上 cookies,需要把withCredentials设置为true

我们都知道服务器设置 cookie 是通过set-cookie的 http header 来完成。浏览器会读取这个信息设置 cookie。但是对于跨与请求,默认是无效的。我们需要再添加一个 http header。

Access-Control-Allow-Credentials: true
1

当设置了这个 http header,Access-Control-Allow-Origin就不能设置为星号了,必须指定具体的网址。我们必须指定withCredentialstrue并且Access-Control-Allow-Credentialstrue的时候,服务器返回的set-cookie才会生效。

参考:

Access-Control-Allow-Credentials (opens new window)

CORS (opens new window)

伪随机的种子问题

最近前端的工作不是很多,所以就帮忙写写后端的 ruby on rails。在写测试用例的时候,虽然单元测试的数据每次都是随机生成的,但是我们需要根据单元测试来生成 API 文档。如果测试文档每次都是随机的数据会很难检查每次 API 更新了什么,所以我们需要在生产测试文档的时候,保证随机数据的稳定性。

# Random seed

seed 的意义就在于,初始化了随机数生成器。保证了每次随机的结果都一样。例如代码:

@rand = Random.new(1234)

puts @rand.rand

puts @rand.rand(0..1050)

puts @rand.rand(0..1050)

puts @rand.rand
1
2
3
4
5
6
7
8
9

这段代码每次执行都会输出:

0.1915194503788923
674
699
0.2725926052826416
1
2
3
4

# rand range

但是当我们把中间的 rand 的范围稍微修改:

@rand = Random.new(1234)

puts @rand.rand

puts @rand.rand(10..150)

puts @rand.rand(10..150)

puts @rand.rand
1
2
3
4
5
6
7
8
9

会发现输出发生了变化:

0.1915194503788923
48
63
0.4377277390071145
1
2
3
4

第一次的输出还是不变,当然 2,3 两次肯定是变化的,重点是最后一次。我们发现最后一次的随机数也发生了变化。

# 总结

通过调查 ruby 源码发现,对于有范围的 rand。ruby 会不断生成随机数,判断是否落在区间范围内,如果是在范围内就返回,否则继续尝试。默认在 0 到 1 之间是百分百命中,所以是随机一次。如果是其他范围,命中次数不一样会导致生成随机数的次数发生变化。所以影响到之后的随机数生成。

参考:

ruby (opens new window)

Random (opens new window)

Web离线的解决方案

最近的工作是做嵌入在 iOS 程序内部的页面,其中有一个需求就是需要满足在离线的情况下显示页面。当然,现在主流的离线方式是使用 Service Worker 来完成离线需求。 但是 iOS 内置的 WKWebView 并不支持最新的 Service Worker(取决于 iOS 版本)最新版本已经支持,所以不得不想办法来解决。

# Application Cache

AppCache 是一个过时的技术,但是在 iOS 下勉强还能用。不过 Chrome 对这种过时的技术支持不是很好,单个缓存文件最大只支持 5MB。而且还有请求的 BUG:Accept header on GET request for appcache manifest (opens new window)。触发 AppCache 是在 html 标签中添加 manifest 属性。

<html manifest="manifest.appcache">
  ...
</html>
1
2
3

通过manifest.appcache文件来定义需要缓存的文件,不过载入manifest.appcache的页面会被作为Master entries缓存起来。因为 AppCache 出现的时候还是以静态网站为主,所以并不能缓存请求的 Ajax 数据。我们需要自己再手动实现缓存所有请求在localStorage,但是localStorage同样也有最大 5MB 的限制。所以也需要考虑相应的解决方案。不过,我最终采取的方式是通过嵌入iframe来实现触发 AppCache,这样做的好处是因为 SPA 应用是自己来控制路由的,所以导致每个路径都会保存一份Master entries。但是通过 iframe 的话,我们的Master entries永远只有一份。并且当前页面的所有资源也被顺利缓存,因为 SPA 无论访问哪个路径返回的都是相同的index.html。由 JS 来控制路由并加载相应的组件。

这里要补充的一点是,针对 https 的 Application Cache 是无法完成跨域请求的,所以请慎重。

# WorkBox

Service Worker 是现在主流的缓存技术,会帮你缓存所有的静态文件和数据请求。但是对于 SPA 项目,我们不可能自己手动书写缓存清单。所以还是借助现有的开源解决方案,这里最出名的解决方案是谷歌的 Workbox。通过使用 Webpack 插件和简单的配置,我们便可以做到缓存所有的静态资源和数据。Service Worker 有很多种缓存策略可以选择,例如 Cache First 和 NetWork First。但是 AppCache 每次都会优先使用缓存,然后再去更新最新的文件。所以我们不得不在发生更新的时候去重新加载页面。

# 总结

最终的解决方案是优先使用 Service Worker,当不支持 Service Worker 的时候再回退到 AppCache。但是想要从 AppCache 升级到 Service Worker 的时候,必须清除所有 AppCache 的所有数据。浏览器并没有提供相应的接口,我们目前采用的方式是手动删除 AppCache 储存的数据库来完成这一需求。

参考:

Using the application cache (opens new window)

HTML5 Offline Application Cache (opens new window)

Application Cache plugin for Webpack (opens new window)

Workbox webpack Plugins (opens new window)

Appcache Facts (opens new window)