NuxtJS 代理

Vue 中可以设置代理,但它的代理都是本地 Node 代理,只在开发中用,解决开发过程中跨域之类的问题;

Nuxtjs 则不同,服务端渲染应用部署方式下,代理功能要随页面发布到线上;如果是静态应用部署方式,功能就和 Vue 一样了,只在开发中用;

这里主要讨论服务端渲染应用部署方式下的代理;

0、代理和反向代理

代理这个词生活中还是比较常见的,比如“代理人”,一般指可以帮我们去做某些事情的人;

针对 Web 程序,代理和反向代理大体是这个意思:

代理

主体(主语)是页面;页面让其他程序来帮他获取数据,就叫代理;如果这个“其他程序”是一个服务器,就可以叫他代理服务器;

开发中常常用来解决跨域问题,页面直接请求报跨域错误,让开了跨域的服务器帮页面去获取数据;

反向代理

主体(主语)是服务器;服务器收到一个请求,然后让其他程序(或其他服务器)来处理,最后得到一个处理结果,再响应刚才的请求;

反向代理的一般判断逻辑是通过请求 url 做区分(代理也是如此),比如 a.com 这个服务器接收到两个请求,http://a.com/api/v1/getList 和 http://a.com/api/v2/getList,/api/v1/ 可以让A来处理,/api/v2/ 可以让B来处理;

1、asyncData 做代理

放在 asyncData 中的请求,会自动转换成 node 端调用(代理去调用接口),然后将返回数据设置到 data 属性中,无需特殊处理;

但是,上面的情况只发生在第一次进站内页面或刷新页面的时候,如果还是跳站内页,asyncData 中的请求会以 ajax 形式发送,不再是 node 端调用接口并渲染了(这个我还没搞懂为什么,且并未从官方文档里找到相关解释;暂时的推测是:为了让页面加载更快吧),所以这时候 node 的自动代理不起作用,跨域问题又暴露出来了;

2、@nuxt/proxy 方案

官方有一个 proxy 的库,地址在这里;需要在 nuxt.config.js 中进行配置,一种语法是下面这样:

modules: ['@nuxtjs/proxy',],
proxy: {
  '/news': 'https://a.com/api/',
},

上面的配置项,表示 /news/ 开头的请求路径,都代理到 http://a.com/api/ 这个地址上;转换关系为:

/news/getlist ==> http://a.com/api/news/getlist

缺点:只能用于页面ajax异步请求的代理,asyncData 中 node 转发的情况就不适用了;

3、服务端手动判断并转发

一般在创建项目时选了 Server Framework 才会用到;选了框架会生成 server/index.js,具体逻辑就可以写在这里了;

现在有一个接口 http://xxx.com/news/getlist,设计成代理的方式,“页面请求node服务器,node服务器去请求 xxx.com/news/getlist” 这种模式下,页面请求可以加一个前缀用来标识需要 node 代理的接口,比如 /proxy/,node 匹配到 /proxy/ 开头的 path,就进行代理操作;

如果用 Koa 框架,server/index.js 的 start 方法 app.use 一行,做如下改造(用路径前缀 /proxy/ 表示需要代理的接口)

// use 中的函数改为异步 async
app.use(async (ctx) => {

  // 本次变动添加代码
  let url = ctx.request.url;
  if (/\/proxy\//gmi.test(url)) {  // 需要本机代理的ajax接口,来源:页面ajax异步调用
    let link = url.replace(/\/proxy/gmi, '');
    let res = await serverAxios.get(link);
    ctx.response.body = res.data;
    return;
  }

  // 默认生成代码
  ctx.status = 200
  ctx.respond = false // Bypass Koa's built-in response handling
  ctx.req.ctx = ctx // This might be useful later on, e.g. in nuxtServerInit or with nuxt-stash
  nuxt.render(ctx.req, ctx.res)
})
app.listen(port, host)

如果是用 express,代码大体是这样(同样用路径前缀 /proxy/ 标识代理接口):

const app = express()
const router = express.Router();
router.get('/proxy/', function (req, res, next) {
  let url = req.url;
  res.json({a:1});
  res.end();
});
app.use(router);

前端页面就是这样发请求:

const axios = require('axios');
axios.get('/proxy/news/getlist').then(function(response){
  // 响应成功,返回数据
});

*实际项目中,Koa 框架下,Node 服务端代码中,我用了 koa-router 来实现代理请求的匹配 ,yarn dev 时没有问题,可以正常代理转发接口,yarn build、yarn start 之后,页面上的 ajax 请求就 404 了,地址是对的,但 node 端没有匹配该请求,最终也没找到问题,代码是这样的:

const Router = require('koa-router'); // koa 路由中间件
const router = new Router(); // 实例化路由
// 代理 /proxy/api/ 请求,/proxy/ 相当于一个标识符,实际请求是不带的
router.get('/proxy/api/*', async (ctx, next) => {
  let link = ctx.request.url.replace(/\/proxy/gmi, '');
  let res = await axios.get(link);
  ctx.response.body = res.data;
});

自己的测试项目中是没有问题的,对应下面这个提交:

Commits on May 29, 2020 “实现功能:node代理(页面ajax)接口;方案1.1……”

使用的时候注意下吧,如果 build、start 之后 404,可以考虑是否为这种情况;

4、serverMiddleware 方案

serverMiddleware 官方文档在这里

不要将它与客户端或SSR中Vue在每条路由之前调用的 routes middleware 混淆。
serverMiddleware 只是在 vue-server-renderer 之前在服务器端运行,可用于服务器特定的任务,如处理API请求或服务资产。

nuxt.config.js 中,添加 serverMiddleware 属性,可以为特定 path 指定一个处理文件:

serverMiddleware: [
  {path: '/proxy', handler: '@/server/proxy.js'},
],

server/proxy.js 代码:

const serverAxios = require('./serverAxios');

export default async function (req, res, next) {
  // req 是 Node.js http request 对象
  // res 是 Node.js http response 对象
  let res1 = await serverAxios.get(req.url);
  res.setHeader('Content-Type','application/json;charset=utf-8');
  res.end(JSON.stringify(res1.data));
 
  // next是一个调用下一个中间件的函数
  // 如果您的中间件不是最终执行,请不要忘记在最后调用next!
  // next();
}

前端页面还是这样发请求:

const axios = require('axios');
axios.get('/proxy/news/getlist').then(function(response){
  // 响应成功,返回数据
});

和 “3、服务端手动判断并转发” 方案不同的是,这里的处理程序 server/proxy.js 中,传过来的 req.url 不带 /proxy 前缀,已经自动去掉了,无需再手动处理;


3和4的方案扩展性更强,可以在转发之前做一些操作;

比如接口需要进行一些验证,才能返回数据,可以在转发的时候带上token,

如果这篇文章对你有用,可以点击下面的按钮告诉我

0

发表回复