挑战21天手写前端框架 day15 Proxy 代理,前端代理,开发代理

713 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情

原理

代理也称网络代理,是一种特殊的网络服务,允许一个终端(一般为客户端)通过这个服务与另一个终端(一般为服务器)进行非直接的连接。- 维基百科

在项目开发(dev)中,所有的网络请求(包括资源请求)都会通过本地的 server 做响应分发,我们通过使用 http-proxy-middleware 中间件,来代理指定的请求到另一个目标服务器上。如请求 featch('/api') 来取到远程 http://jsonplaceholder.typicode.com/ 的数据。

要实现上述的需求我们只需要在配置文件中使用 proxy 配置:

export default {
  proxy: {
    '/api': {
      'target': 'http://jsonplaceholder.typicode.com/',
      'changeOrigin': true,
      'pathRewrite': { '^/api' : '' },
    },
  },
}

上述配置表示,将 /api 前缀的请求,代理到 http://jsonplaceholder.typicode.com/,替换请求地址中的 /api'',并且将请求来源修改为目标url。如请求 /api/a,实际上是请求 http://jsonplaceholder.typicode.com/a

一般我们使用这个能力来解开发中的跨域访问问题。由于浏览器(或者 webview)存在同源策略,之前我们会让服务端配合使用 Cross-Origin Resource Sharing (CORS) 策略来绕过跨域访问问题。现在有了本地的 node 服务,我们就可以使用代理来解决这个问题。

XMLHttpRequest cannot load api.example.com. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access.

原理其实很简单,就是浏览器上有跨域问题,但是服务端没有跨域问题。我们请求同源的本地服务,然后让本地服务去请求非同源的远程服务。需要注意的是,请求代理,代理的是请求的服务,不会直接修改发起的请求 url。它只是将目标服务器返回的数据传递到前端。所以你在浏览器上看到的请求地址还是 http://localhost:8000/api/a

值得注意的是 proxy 暂时只能解开发时(dev)的跨域访问问题,可以在部署时使用同源部署。如果在生产上(build)的发生跨域问题的话,可以将类似的配置转移到 Nginx 容器上。

实现

安装模块


cd packages/malita
pnpm i http-proxy-middleware

类型定义

我们在 packages/malita/src/config.ts 定义一个配置 proxy 类型

import type { Options as ProxyOptions } from 'http-proxy-middleware';

export interface UserConfig {
    title?: string;
    keepalive?: any[];
+    proxy?: { [key: string]: ProxyOptions };
}

使用 http proxy 中间件

import { createProxyMiddleware } from 'http-proxy-middleware';

const buildMain = async ({ appData }: { appData: AppData }) => {
    // 获取用户数据
    const userConfig = await getUserConfig({
        appData, malitaServe
    });

    // 略 getRoutes generateEntry generateHtml

    if (userConfig.proxy) {
        Object.keys(userConfig.proxy).forEach((key) => {
            const proxyConfig = userConfig.proxy![key];
            const target = proxyConfig.target;
            if (target) {
                app.use(
                    key,
                    createProxyMiddleware(key, userConfig.proxy![key],),
                );
            }
        });
    }
}

我们判断如果用户设置了 proxy 就使用 http-proxy-middleware 中间件。

项目中配置使用

examples/app/malita.config.ts

export default {
    title: 'Hello',
    keepalive: [/./, '/users'],
    proxy: {
        '/api': {
            'target': 'http://jsonplaceholder.typicode.com/',
            'changeOrigin': true,
            'pathRewrite': { '^/api': '' },
        }
    }
}

运行验证

cd examples/app

pnpm dev

> malita dev

App listening at http://127.0.0.1:8888
[HPM] Proxy created: /api  -> http://jsonplaceholder.typicode.com/
[HPM] Proxy rewrite rule created: "^/api" ~> ""

仔细看日志说明,就是我们前面提到的将 /api 前缀的请求,代理到 http://jsonplaceholder.typicode.com/,替换请求地址中的 /api'',并且将请求来源修改为目标url。如请求 /api/a,实际上是请求 http://jsonplaceholder.typicode.com/a

浏览器中访问 http://127.0.0.1:8888/api/users

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
]

你会发现我们并没有写任何服务,但是却能够请求到真实的数据。表示我们的代理服务已经成功运行了。

我建议你如果还不是很理解这个代理服务和这些配置的作用,什么代理不代理,替不替换前缀的,你可以多尝试着修改配置,看看真实代理的表现。

感谢阅读,今天的内容就到这里了哦,嘻嘻嘻,记得给我点赞和关注哦。

源码归档