首页与我联系

如何在开发环境中用node代理解决前后端联调跨域问题

By 前端达人
Published in 1-JavaScript
September 17, 2022
1 min read
如何在开发环境中用node代理解决前后端联调跨域问题

最近做的项目,后端API是用的.NET, 前端开发用的是Angular4, 是一个前后端分离的项目。在本地开发的时候,我用node express搭了一个proxy用来解决前后端跨域联调的问题。

最近项目来了新人,每次带新人开发的时候,都会问到为什么要用这个node proxy,本地开发的时候直接调用后端DEV-INT服务器上的API不就好了吗? 每次都解释这个是用来解决联调跨域的,刚工作的同事基本都是懵比状态,有经验的一般会问,不是在服务器端设置好CROS就可以了,但是对于代理实现跨域并不是很了解。在这篇文章里就会解释为什么用要跨域以及如果用node proxy解决联调跨域问题。

为什么会有跨域?

浏览器有一个同源策略, 这里的同源指的是以下三个要一直:

- 协议相同
- 域名相同
- 端口相同

这样做的目的是为了保证用户信息安全,防止恶意的网站攻击。现在如果是非同源,以下三种行为会受到限制:

1. cookie localstorage 和 indexdb 无法读取
2. DOM 无法获得
3. AJAX 请求不能发送

基于浏览器这个同源策略,会导致前后端分离开发项目势必会存在跨域问题,比如前端本地开发的时候request url为:https://localhost:4200/useragents/requesttokens 后端开发写好这个API后发布到DEV-INT服务器上暴露出来的API是:https://expamle.com/useragents/requesttokens, 这样就是域名不一样,端口也不一样,就是非同源,如果在前端代码里直接调用https://expamle.com/api/useragents/requesttokens 我们来看一下会有什么问题:

因为这是一个非简单请求,浏览器在发送真正的请求之前都会发一个预检请求,post method为options。浏览器先询问服务器,当前网络所有在的域名是否在服务器许可名单之中,以及可以使用哪些HTTP方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。从上面截图的错误信息可以看出来,服务器端没有设置CROS跨域,这就是我们说的跨域问题。

node proxy来解决跨域问题

这个代理具体做以下几个步骤:

1. 接受客户端请求
2. 将请求转发给服务器
3. 拿到服务器响应数据
4. 将响应数据转发给客户端

具体代码如下:

//app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var request = require('request');
var cors = require('cors');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));


function setResHeader(res) {
  res.setHeader('Access-Control-Allow-Origin', '*');
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); // If needed
  res.setHeader('Access-Control-Allow-Headers', 'Authorization,Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers,RequestID,X-Content-Type-Options,X-Content-Type-Options,X-Frame-Options,X-Powered-By,X-Version,x-xss-protection,Strict-Transport-Security') // If needed
  res.setHeader('Access-Control-Allow-Credentials', true); // If neede
}

app.options('/**', cors(), function (req, res) {
  this.setResHeader(res);
    var redirectURL = 'http://example.com/api' + req.path;
    res.send(redirectURL);
});

app.post('/**', function (req, res, next) {
  this.setResHeader(res);
  var redirectURL = 'http://example.com/api' + req.path;

  request.post({ url: redirectURL, headers: req.headers, body: JSON.stringify(req.body) }, function (error, response, body) {
    if (response) {
      if (response.statusCode !== 200 && response.statusCode !== 201) {
        res.status(response.statusCode).send(response.body ? JSON.parse(response.body) : '');
      } else {
        res.send(response.body);
      }
    }
    else {
      next(createError(404));
    }


  });
});

app.get('/**', function (req, res, next) {
  this.setResHeader(res);
  var redirectURL = 'http://example.com/api' + req.originalUrl;
  request.get({ url: redirectURL, headers: req.headers }, function (error, response, body) {
      if (response) {
        if (response.statusCode !== 200 && response.statusCode !== 201) {
          res.status(response.statusCode).send(response.body ? JSON.parse(response.body) : '');
        } else {
          res.send(response.body);
        }
      }
      else {
        next(createError(404));
      }

    });
});

app.use(function (req, res, next) {
  next(createError(404));
});


// error handler
app.use(function (err, req, res, next) {
  // set locals, only providing error in development
  res.locals.message = err.message;
  res.locals.error = req.app.get('env') === 'development' ? err : {};

  // render the error page
  res.status(err.status || 500);
  res.render('error', {
    message: err.message,
    error: err
  });
});

module.exports = app;

同时在WWW.js文件中设置好监听端口为9001

var port = normalizePort(process.env.PORT || '9001');
app.set('port', port);

然后在package.json文件中设置好命令行:

  "scripts": {
    "node:9001": "node ./bin/www"
  },

前端在调用API的时候需要把API endpoint设置为http://localhost:9001

这样就解决了跨域问题, node会把监听到的API都转发到后端服务器,node和后端服务器就不存在跨域这种说法,所以可以直接调用。跨域只是针对浏览器同源策略。

文章转自:https://limeii.github.io/2019/04/js-node-proxy/ 作者:Li Mei

前端达人公众号.jpg 欢迎关注「前端达人」公众号


Tags

nodejsbasic
Previous Article
动手练一练,深入学习 4 个与 Hover 相关的动效案例 (下)
前端达人

前端达人

专注前端知识分享

相关文章

「短文」如何清空 JavaScript 数组的内容
September 22, 2022
1 min

前端站点

VUE官网React官网TypeScript官网

公众号:前端达人

前端达人公众号