问题解决
开发中我们可能会遇到展示文件上传进度的需求,原生的 XHR 请求是支持进度回调,axios 有 onUploadProgress 事件,而 fetch 不支持进度。所以要留意一下你的公司内部请求库是基于哪个封装的,才能顺利使用进度回调。
axios({ // 用法
url: 'http://...',
method: 'POST',
data: data,
onUploadProgress: handlerProgress
})
然而在实际项目中,我遇到了这种情况,正常使用 axios 的 onUploadProgress 事件没问题,但是断开网络后,onUploadProgress 还是会有 loaded 进度产生。于是上网搜索,axios 官方 issue 上也有人提过这个问题,但是没有给出回答。而且,令我纳闷的还有,在拔掉网线的情况下,我点击上传,network 下面的请求不是直接标红报网络错误,而是 pendding 了好久才报错。正常断网不是应该发布出去请求么,而且 onUploadProgress 居然还返回了已上传了多少多少字节数据?
在我找到问题之前,我一度以为这是 axios 存在的 bug,后来才意识到可能是代理引起的。我的项目使用的是 umi 搭建,为了开发环境下实现跨域,我没有选择设置浏览器安全策略,而是选择了 umi 提供的 proxy 代理。于是我的请求都是先请求了本地 localhost ,再由代理转发到远程后端服务,这样一来就实现了跨域请求。
其实问题也不难被发现,打开控制台 network 查看,请求的 url 其实是本地的:
那就好理解了,前端发送上传请求到本地,本地接收文件流的同时向服务器发送请求和文件流,在断网的情况下,前端请求可以发送到本地,但是本地的发送不了给服务端,会报错,导致请求失败。onUploadProgress 仅有的一点已加载的数据其实是发送给本地服务的数据。这也就能解释为什么 onUploadProgress 在断网的情况下还能回传进度了。
关于跨域和反向代理
由于浏览器有同源策略,客户端访问和自己非同源的服务器(协议、ip、端口),如果不做任何设置,是会被拒绝的。解决跨域的方式很多:jsonp、iframe、浏览器设置、服务端设置响应头(CORS)、反向代理等等。这里说一下我对反向代理的理解。
关于正向代理和反向代理的区别:
- 正向代理:客户端知道真正的服务器地址,而服务器不知道真正的客户端是谁,只知道代理服务器地址;
- 反向代理:客户端只管访问代理服务器,而真实请求被代理到哪里是未知的;
实际上正向代理隐藏了真实的客户端,而反向代理隐藏了真实的服务端。反向代理可以用来处理服务器的负载均衡以及解决跨域问题。
反向代理如何解决跨域问题:
在使用 Nginx 部署项目的时候,我们开启某个端口对外提供服务,但是用户在访问网站的时候,一般不会记得网站具体的端口,于是通常情况下我们都是将网站部署在默认端口80下。但是后台服务的地址肯定不会是 80,那这样虽然前后端是在同一个ip下也会存在跨域。
Nginx 允许我们配置一个代理,将80端口下的特定路由的请求代理到对应的真实服务器上,也就是说客户端发送 /api/getInfo 这个请求的时候先访问本地端口下的服务,通过Nginx代理到了真实服务器上,并作出响应。
不光 Nginx 可以做反向代理,前端框架像 Vue、umi 都可以配置反向代理来解决跨域问题,当然这种配置是解决开发环境下的跨域问题(不用我们去手动设置浏览器的属性了)。