上节课我们简单介绍了如何通axios发送请求,以及发送请求的跨域问题。从而引出了通过代理来解决跨域问题,但是我们也说了配置代理总共有两种方法,上节课是最简单的一种,那么这节课我们来讲另一种。

回顾

在开始讲新的这一种方法之前,我们来对上节课的内容来做一个简单的回顾。

  • react本身只关注界面,不包含发送ajax请求的代码
  • 前端通过ajax请求与后台交互(json数据)
  • react应用中需要集成第三方ajax库(或者自己封装)
  • 不推荐使用jQuery,推荐使用axios
  • 通过配置代理解决跨域问题
  • 代理端口一定要与客户端端口一致

以上便是上节课的主要内容,接下来我们来开始介绍第二种配置代理的方法。

配置需求场景

可能有人会觉得好奇了,我们上节课的那种方法不是挺好的嘛,有简单又有效,但是如果我们有多个代理要配置怎么办?

比如说这一个项目太大了,总共开了两个后端服务,这两个后端服务的端口肯定不一样啊,那么我们是不是要配置多个代理?那么上节课讲得第一种方法是不是就无法满足条件了?那么我们这节课要介绍的第二种方法变可以应对这种场景。

场景需求

现在我们有另一个测试接口,端口开在 5001,这个接口是用来获取车的信息。我们来看一下代码

export default class App extends Component {
  render() {
    return (
      <div>
        <button onClick={this.getData}>Click</button>
        <button onClick={this.getCarData}>Click</button>
      </div>
    );
  }
  getData = () => {
    axios.get("http://localhost:3000/students").then(
      response => {console.log("success",response.data);},
      error => {console.log("error",error);}
    )
  };
  getCarData = () => {
    axios.get("http://localhost:3000/car").then(
      response => {console.log("success",response.data);},
      error => {console.log("error",error);}
    )
  };
}

现在我们组件中有两个按钮,第一个绑定onClick事件并讲getData方法作为事件回调,第二个按钮绑定onClick事件,并用getCarData方法作为事件回调,那么是不是就意味着我们点击第一个按钮,请求第一个在 5000 端口的接口,点击第二个按钮,对应的就去请求在 5001 端口的接口?那我们应该怎么配置呢?还是改package.json?不可能啊,package.json里面不可能出现两个proxy字段啊,首先在json语法上就不对了。那么我们应该怎么处理?

多代理配置

如果我们想要配置多个代理,我们就不能在package.json里面配置了,我们把原来配置的先删掉。我们如果想要在脚手架中来配置多个代理的话,我们需要通过在src中编写一个文件叫setupProxy.js来完成配置,而且这个文件的名字绝对不允许修改,否则将不起任何作用,而且在该文件里面我们将不能使用ES6语法。因为脚手架会自动找到这个文件并加到webpack的配置中去,而webpack使用的是node.js里的语法,所以这个文件中得使用CJS语法来编写。

那么我们来看一下:

const {createProxyMiddleware} = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1", {
      target: "http://localhost:5000",
      changeOrigin: true,
      pathRewrite: { "^api1": "" }
    })
  );
}

首先我们来看一下代码,第一步引入http-proxy-middleware中间件,这个中间件在外面创建项目的时候脚手架就自动帮我们安装好了,因为我们这里是CJS语法所以不能再用import了,然后我们要导出一个对象,但是现在新版要求我们写成一个函数,那么我们就写成函数吧,然后这个函数会接收到一个服务对象,我们用app参数接一下。

然后在函数体内我们需要调用app上的一个使用的方法,那么我们使用什么?我们既然要配代理那是不是就要使用代理啊使用使用createProxyMiddleware,调用的时候要传两个参数,第一个就是我们的请求前缀,我们有多个服务器要请求,那么我得用一个前缀来标识那些请求对应哪个服务器啊。当我们写了前缀之后,就代表这以后所有包含这个前缀的请求都转发给我们指定的服务器。那么转发给谁呢?这就引出了我们的第二个参数。

第二个参数得接收一个配置对象。这个对象里面要有一个属性target,我得指定这个请求转发给谁啊,然后下面一个属性changeOrigin是什么意思?这个属性能够让服务器明确知道这个请求是从哪发送的,然后pathRewrite,这个是什么?是不是url路径改写?如果我们不写这个是不是连带着url前缀一起就转发给服务器了?那么服务器中的接口url有写这个前缀吗?这个前缀是我们自己写的,服务器压根就不知道这么个玩意儿。所以说如果不写pathRewrite的话即便请求转发了那肯定会 404 的。那么这些事儿都做完是不是就可以发送请求了呢?我们来试一下。先重启脚手架,然后点击按钮请求接口:

image-20220111140624642

是不是报错了?404 为什么 404?我们是不是没有改 App组件中的url?我们没有加前缀啊,没有加前缀的话就识别不出来有代理指定啊。所以我们来改一下App组件:

export default class App extends Component {
  render() {
    return (
      <div>
        <button onClick={this.getData}>Click</button>
        <button onClick={this.getCarData}>Click</button>
      </div>
    );
  }
  getData = () => {
    axios.get("http://localhost:3000/api1/students").then(
      response => {console.log("success",response.data);},
      error => {console.log("error",error);}
    )
  };
  getCarData = () => {...};
}

我们先改了getData方法,因为我们目前只加了一个代理是转发到 5000 端口的,只有getData是去 5000 端口拿数据的,那么我们来看一下这次是不是可以成功呢?

image-20220111141054553

这次我们成功拿到了数据,那么现在我们是成功添加了一个代理,那么另一个代理怎么添加呢?再回到setupProxy.js中,app.use方法是可以接收很多个参数的,我们在加一个createProxyMiddleware的调用就可以了啊。

const { createProxyMiddleware } = require('http-proxy-middleware');

module.exports = function (app) {
  app.use(
    createProxyMiddleware("/api1", {
      target: "http://localhost:5000",
      changeOrigin: true,
      pathRewrite: { "^/api1": "" }
    }),
    createProxyMiddleware("/api2", {
      target: "http://localhost:5001",
      changeOrigin: true,
      pathRewrite: { "^/api2": "" }
    })
  );
}

我们再看,我们两次调用createProxyMiddleware是不是都没用什么区别,就是改了端口号和url前缀,那么既然加了第二个代理,那么getCarData方法中的url是不是也要跟着改一下前缀,那么我们现在再看一下效果:

image-20220111141548518

这一次我们两个按钮的请求是不是都成功拿到数据了。

setupProxy的属性讲解

我们看一下我们之前写的这一部分:

createProxyMiddleware("/api1", {
  target: "http://localhost:5000",
  changeOrigin: true,
  pathRewrite: { "^/api1": "" }
})

首先这些属性都是什么意思呢?

我们先看那个/api1这个是什么?我们说了是url前缀,那么是干什么的?我们开了代理,但是我们也不能什么都走代理吧,如果说我们要获取前端自身的东西,肯定就不会去走代理的,所以说这个url前缀的用途就是控制哪些请求走代理。

在看看target属性,这个就简单了,我代理是转发请求的,我得通过这个属性来告诉代理服务器把这个请求发给谁。

那么changeOrigin属性又是什么呢?这个属性其实默认值是false,但是我们要求尽量都改成true,为什么?首先这个属性是用来控制服务器收到的请求头中Host的值的。如果该属性为false我们来看一下:

image-20220111142604480

服务器知道这个请求是localhost:3000发过来的,但是如果我们加了changeOrigin属性,而且设为true之后在请求会什么样子呢?

image-20220111142735093

这一次不一样了,服务器获取到的host就变成了localhost:5000,所以说我们最好加上这个属性。

那么再来看pathRewrite属性,如果我们不加这个属性的话会变成什么样子?我们之前是不是也说了会报 404?因为走代理会去服务器请求/api1/students,但是服务器没有这个url其实。那么我们来验证一下:

image-20220111143112365

首先是不是 404 了?然后我们再看一下啊服务端:

image-20220111143142519

是不是跟我们说的一样?我们是到服务器上去请求/api1/students,但是服务器端并没有这个url所以说就会报 404。

以上便是配置代理的第二个方法

总结

  • 配置多个代理不能在package.json里面配置
  • src中编写一个文件叫setupProxy.js来完成配置多个代理
  • setupProxy.js文件名固定不运行改
  • setupProxy.js文件得使用CJS语法来编写
  • url前缀来指定请求走代理
  • target属性来指定转发的服务器
  • changeOrigin属性尽量设为true
  • pathRewrite属性是必须的,否则会报 404
  • 可以通过在use中多次调用createProxyMiddleware来配置多个代理

Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.

Link of this article:https://work.lynchow.com/article/proxy_of_scaffold_2/