一种方便的跨域开发解决方案

如今愈来愈多的 Web 项目都采起先后端分离的开发方式,也就是在开发过程当中前端工程运行在一个 node server 上,同时提供 REST API 的后端工程做为独立的服务运行在另外一个 server 上,这样先后端经过 HTTP 请求进行通讯的时候就会遇到跨域的问题,跨域是怎么一回事你们应该都了解,这里就再也不赘述。以往解决跨域问题比较经常使用的方法就是给服务端配置 CORS,可是这种方式会带来一些不便:

  1. 要修改后端代码,若是后端项目运行在本身机器上还好,若是是运行在别的服务器上,甚至是其余团队在维护,那么修改一段代码就会比较麻烦,而且还要确保这些代码不会被发布到生产环境中去。
  2. 每次启动浏览器的时候要输入一长串代码来关闭浏览器的安全策略,好比 Chrome 浏览器的命令以下: open -a /Applications/Google\ Chrome.app --args --disable-web-security --user-data-dir 不只麻烦,万一一不当心在这种模式下访问了一些敏感的数据,还会带来安全隐患。
  3. 开发模式下前端调接口要带上测试后端服务器的 URL,所以发布到生产环境以前还要把它去掉。

总之就是比较麻烦,体会不到那种脱了裤子就上,完事提上裤子就走的爽快感,我说的是上厕所。因此今天就是要介绍一种更加简单安全的解决方案,同时咱们会深刻去了解其中的原理是什么。html

首先,用 create-react-app 建立一个前端项目,假如你的前端项目运行的地址是 http://localhost:3000,与此同时提供 API 的后端项目运行的地址是 http://localhost:4000,你要作的只是在前端工程的 package.json 文件中添加这样一行配置:前端

"proxy": "http://localhost:4000"
复制代码

而后你就会神奇地发现,从前端页面发出的 HTTP 请求,虽然访问的依然是 3000 端口,可是会被自动转发到 4000 端口的后端服务器并获得正确的响应,于此同时访问页面的请求却不会被转发,依然可以被前端路由捕获,这样咱们就彻底不须要再考虑如何处理跨域的问题了。问题是解决了,可是又出现了 2 个问题萦绕在个人心中:node

  1. package.json 中的 proxy 参数是做用在什么地方的?
  2. 是怎么样作到把访问页面的请求和访问 REST API 的请求区分开的?

带着这样的疑问咱们一块儿去看看 create-react-app 的源码是怎样写的,首先在前端项目中的 package.json 里咱们能看到,项目启动执行的脚本是 react-script start,因此咱们打开文件 create-react-app/packages/react-scripts/scripts/start.js(为什么直接能定位到这个文件,以及 react-script 这个命令是如何注册的,属于其余知识点,本文不展开说明,有疑问的童鞋能够去这里 学习一个),咱们看到有如下代码:react

// Load proxy config
const proxySetting = require(paths.appPackageJson).proxy;
const proxyConfig = prepareProxy(proxySetting, paths.appPublic);
// Serve webpack assets generated by the compiler over a web sever.
const serverConfig = createDevServerConfig(
    proxyConfig,
    urls.lanUrlForConfig
);
const devServer = new WebpackDevServer(compiler, serverConfig);
复制代码

此处的 paths.appPackageJson 的声明在 create-react-app/packages/react-scripts/config/paths.js 中:webpack

module.exports = {
  appPackageJson: resolveApp('package.json'),
};
复制代码

所以咱们就知道,这里的 proxySetting 就是咱们以前在前端工程的 package.json 中定义的 proxy 的值,而后咱们看到,proxySetting 被用来生成了 serverConfig,最终 serverConfig 做为配置参数建立了 WebpackDevServer 实例。webpack-dev-server 是一个用于启动 webpack 的测试服务器,而且提供了诸如 HMR 等方便开发的功能,所以咱们就得出第一个结论:前端工程的 package.json 中定义的 proxy 值,是做用于 WebpackDevServer,最终经过 WebpackDevServer 进行的转发git

让咱们继续试图解答第二个问题——是怎么样作到把访问页面的请求和访问 REST API 的请求区分开的?咱们看到 proxySetting 首先是被传入 prepareProxy 方法获得 proxyConfig,而后在 createDevServerConfig 方法中返回了一个对象,而且对象的 proxy 字段的值为 proxyConfig,最终该对象就是 webpack-dev-server 的配置项,在 webpack-dev-server 文档 中能够看到 proxy 的做用就是作一层代理,把从页面来的请求转发到另外一个地址,所以关键就在于 proxyConfig 的配置是怎么样的,因而目光转移到 prepareProxy 方法,prepareProxy 方法的定义在 create-react-app/packages/react-dev-utils/WebpackDevServerUtils.js 中,在这里咱们能够看到首先是对 proxy 进行了类型和格式的检测,而后若是 proxy 是一个格式正确的字符串,就返回一个只有一个对象元素的数组,在这个对象中的 context 字段中出现了以下的判断:github

context: function(pathname, req) {
    return (
        req.method !== 'GET' ||
        (mayProxy(pathname) &&
         req.headers.accept &&
         req.headers.accept.indexOf('text/html') === -1)
    );
}
复制代码

在这里咱们看到有对 req.headers.accept 进行判断,req.headers.accept 用于表示浏览器经过此次 HTTP 请求但愿获取到的内容类型,所以若是 accept 中带有 text/html 则说明本次请求获取的是一个 document,所以就不该该被转发到后端,这一堆判断逻辑用一幅图表示出来以下:web

在这里 context 的含义在 webpack-dev-server 文档中是找不到的,它的说明出如今 http-proxy-middleware 中,context 支持传入一个 function 用于自定义转发的逻辑,只在返回值为 true 时才转发请求, 所以该代理将只会转发 ajax 或者 fetch 发出的 HTTP 请求 。

除了文中提到的这种最简单的配置,webpack-dev-server 的 proxy 还支持多种配置方式以同时知足多种代理规则,感兴趣的同窗能够去文档里面了解更多细节。ajax

相关文章
相关标签/搜索