深入理解 webpack proxy
一、webpack proxy 的定义
在实际项目中不可避免会遇到跨越问题,webpack 中的 proxy 就是解决前端跨域的方法之一。
webpack devServer.proxy 即 webpack 提供的代理服务,基本行为就是接收客户端发送的请求后转发给其他服务器,以解决跨域问题。
想要实现代理首先需要一个中间服务器,webpack 中提供服务器的工具为 webpack-dev-server:
devServer: {
hot: true, // 热更新:只更新改变的组件或者模块,不会整体刷新页面
open: true, // 是否自动打开浏览器
proxy: { // 配置代理(只在本地开发有效,上线无效)
"/x": { // 请求接口中要替换的标识
target: "https://api.bilibili.com", // 被替换的目标地址,即把 /api 替换成这个
pathRewrite: {"^/api" : ""},
secure: false, // 若代理的地址是 https 协议,需要配置这个属性
},
'/api': {
// 本地用 node 写的一个服务,用 webpack-dev-server 起的服务默认端口是 8080
target: 'http://localhost:3000',
// 后台在转接的时候 url 中是没有 /api 的
pathRewrite: {"/api" : ""},
// 加了这个属性,那后端收到的请求头中的 host 是目标地址 target
changeOrigin: true,
},
}
}
// 请求接口文件
// 会报跨域的错,因为本地的是 http://localhost:8080/
fetch('/x/web-interface/nav')
.then((res) => res.json)
.then((data) => {
console.log(data)
})
let xhr = new XMLHttpRequest()
xhr.open('get', '/api/user', true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
console.log(xhr)
console.log(xhr.response)
}
}
xhr.send(null)
二、webpack proxy 工作原理
proxy 工作原理是利用 http-proxy-middleware 这个 http 代理中间件,实现请求转发给其他服务器。举个例子:
在开发阶段,本地地址为 http://localhost:3000,该浏览器发送一个前缀带有 /api 标识的请求到服务端获取数据,但响应这个请求的服务器只是将请求转发到另一台服务器中:
const express = require('express')
const proxy = require('http-proxy-middleware')
const app = express()
app.use('/api', proxy({ target: 'http://www.example.org', changeOrigin: true }))
app.listen(3000)
// http://localhost:3000/api/foo/bar -> http://www.example.org/api/foo/bar
三、webpack proxy 使用场景
1、场景一
请求到 /api/xxx 现在会被代理到请求 http://localhost:3000/api/xxx, 例如 /api/user 现在会被代理到请求 http://localhost:3000/api/user:
mmodule.exports = {
//...
devServer: {
proxy: {
'/api': 'http://localhost:3000'
}
}
}
2、场景二
如果想要代码多个路径代理到同一个 target 下, 可以使用由一个或多个「具有 context 属性的对象」构成的数组:
module.exports = {
//...
devServer: {
proxy: [
{
context: ['/auth', '/api'],
target: 'http://localhost:3000'
}
]
}
}
3、场景三
如果不想始终传递 /api,则需要重写路径:
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
pathRewrite: { '^/api': '' }
}
}
}
}
请求到 /api/xxx 现在会被代理到请求 http://localhost:3000/xxx, 例如 /api/user 现在会被代理到请求 http://localhost:3000/user。
4、场景四
默认情况下,不接受运行在 HTTPS 上,且使用了无效证书的后端服务器。如果想要接受,只要设置 secure: false 即可。修改配置如下:
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'https://other-server.example.com',
secure: false
}
}
}
}
5、场景五
有时不想代理所有的请求。可以基于一个函数的返回值绕过代理。
在函数中可以访问请求体、响应体和代理选项。必须返回 false 或路径,来跳过代理请求。
例如:对于浏览器请求,想要提供一个 HTML 页面,但对于 API 请求则保持代理。可以这样做:
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: 'http://localhost:3000',
bypass: function (req, res, proxyOptions) {
if (req.headers.accept.indexOf('html') !== -1) {
console.log('Skipping proxy for browser request.')
return '/index.html'
}
}
}
}
}
}
四、webpack proxy 解决跨域的原理
在开发阶段,webpack-dev-server 会启动一个本地开发服务器,应用在开发阶段是独立运行在 localhost 的一个端口上,而后端服务又是运行在另外一个地址上,所以在开发阶段中,由于浏览器同源策略的原因,当本地访问后端就会出现跨域请求的问题。
通过 webpack proxy 实现代理请求后,相当于浏览器与服务端中添加一个代理者。
当本地发送请求的时候,代理服务器响应该请求,并将请求转发到目标服务器,目标服务器响应数据后再将数据返回给代理服务器,最终再由代理服务器将数据响应给本地:
在代理服务器传递数据给本地浏览器的过程中,两者同源,并不存在跨域行为,这时候浏览器就能正常接收数据。
注意:服务器与服务器之间请求数据并不会存在跨域行为,跨域行为是浏览器安全策略限制。