May 16, 2019

记一次本地前后联调的过程

背景

最近项目组的的后端业务开始拆分,把部分用户与业务需求拆分;在原来的时候,用户的请求与业务请求都是由同一个后端项目支持;现在由于子项目不断扩充,不同的子项目之间的账号信息需要同步。

分析

更改前

步骤:

  1. request user, 前端向后端请求用户信息(忽略登录过程)
  2. response token,后端返回用户的token;前端拿到token存储到 storage
  3. req/res 前后端进行业务数据的交流

更改后

步骤:

  1. request user 前端a.exampleuser.example用户后端请求 用户信息(忽略登录过程)
  2. response token,用户后端返回用户的token,设置到 cookie
  3. req/res data 前端与业务后端b.example.com进行数据交流

不同点

这次更改的不同点主要是:

  1. 请求的用户信息的域名不一致;更改后的会存在跨域问题;
  2. 前端存储用户token的方法不同,前者存储在 storage ;后者存储在 cookie
  3. 前者由于用户信息存储在 storage;因为在请求数据的时候,需要在请求拦截器加一层,把用户的 token 加到 xhr 的请求头,让服务器能够拿到对应的用户信息;后者用户信息存储在 cookie;那么前端不需要做特别的处理,cookie 自动加入 header 发送到服务器。

解决方案

由于涉及多个域名,因此原有使用 storage 的方案没有 cookie 那么灵活。OK,涉及多个域名,那么最常见的 CORS 跨域的手段必不可少;前端在这个过程处理不多,只需要设置 xhr 的withCredentials: true;则浏览器会在请求跨域的时候,把对应域名的 cookie 带上到请求。

但是在开发的时候,尽管后端同学已经允许请求跨域;但是看到 network 会出现部分 OPTIONS 请求;然后再发出真正的请求;在这之前,有了解过,这是浏览器的安全机制,先发出 OPTIONS 的预检请求,如果服务器允许的话,才发出真正的请求,这个过程对前端来说是透明的。但是观察到,只是部分请求有预检,并不是所有请求都有预检,那么对于哪些请求才需要呢?然后找到了 mdn cors 相关文章,文章对于情况说的比较明白,这里简单总结几点;

  1. 浏览器对“简单请求”不做预检(preflight);这些请求包括:GET, POST, HEAD 这三种
  2. 请求头(headers)需要在白名单内,除了浏览器自动加的user-Agent,referer等, 白名单的请求头有:
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type(仅包括:application/x-www-form-urlencoded, multipart/form-data, text/plain 三种)
    • DPR
    • Downlink
    • Save-Data
    • Viewport-Width
    • Width
  3. 请求中的任意XMLHttpRequestUpload 对象均没有注册任何事件监听器;XMLHttpRequestUpload 对象可以使用 XMLHttpRequest.upload 属性访问
  4. 请求中没有使用 ReadableStream 对象。

到现在为止,请求就能够正常发出了,用户的登录与业务数据交流也完成了;但实际上也遗留了一个问题:线上环境的业务请求不是跨域的,只是本地与后端同学调试才是,那么能不能本地也不做跨域的请求呢?

后续

本地测试前置条件:

  1. 前端地址:http://localhost:8080
  2. 用户后端项目地址:user.local.com
  3. 业务后端项目地址:a.local.com

由于本地开发的时候,本地通常是访问http://localhost:8080;因此在请求完user.local.com的时候,后端返回的用户信息 cookie 是存在域名 *.local.com;所以当我们请求业务数据的时候a.local.com的时候,浏览器自动把 cookie 带过去。

因此想在请求业务数据a.local.com的时候不跨域而又需要a.local.com;那么本地访问的地址也是要*.local.com;顺理成章,我们把本地的host配一下,a.local.com:8080,这个时候有个疑问,因为不同协议(http/https),不同域名,不同端口的情况下,都属于跨域,那么实际上 cookie 还是不会发过去...

But, 对于我这种面向google的,还是去查一下,因为之前只停留在印象阶段,没有去实际应用;在stackoverflow 的回答中,指出 cookie 的传输不受端口的跨域限制。

至此,我们前端开发的域名就与后端一致了;那么后端的接口怎么发送到后端同学那边呢,这里我们可以使用webpack-dev-server代理我们的请求;webpack dev-server的简略配置

1
2
3
4
5
6
7
8
9
devServer: {
    proxy: {
        // 通常针对特定规则的请求
        '^/backend': {
            // 后端同学的ip地址
            target: '172.x.x.x'
        }
    }
}

总体起来是两点:

  1. 本地配host与测试用户服务器域名为同一个父域名的地址;
  2. 使用webpack-dev-server 代理请求,指定到业务服务器的地址

总结

这次解决的问题实际上也比较简单;但是对一些之前认知的知识只停留在理论阶段,在实际应用上并没有实践好;顺便也巩固之前一些模棱两可的答案。end...