ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

【nodejs基础】解决跨域问题

2022-03-01 12:59:09  阅读:173  来源: 互联网

标签:http 跨域 nodejs res 解决 const data 请求


跨域问题

  • 跨域:浏览器同源策略引起的接口调用问题
  • 同源策略: 主机 端口 协议
  • 接口调用: XMLHttpRequestFetch 都遵循同源策略
  • 浏览器:浏览器发现可疑行为,拒绝接收

浏览器限制跨域请求一般有两种方式:

  • 浏览器限制发起跨域请求
  • 跨域请求可以正常发起,但是返回的结果被浏览器拦截了

一般浏览器都是第二种方式限制跨域请求,那就是说请求已到达服务器,并有可能对数据库里的数据进行了操作,但是返回的结果被浏览器拦截了,那么我们就获取不到返回结果,这是一次失败的请求,但是可能对数据库里的数据产生了影响。

为了防止这种情况的发生,规范要求,对这种可能对服务器数据产生副作用的 HTTP 请求方法,浏览器必须先使用 OPTIONS 方法发起一个预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求。

把这个选项勾上就可以看到预检请求了,关于预检请求,可以参看下面文章。

image.png

image.png

预检请求

https://www.jianshu.com/p/b55086cbd9af

来看看跨域问题是什么样的。

// http.js
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
    const { method, url } = req;
    if (method == 'GET' && url == '/') {
      fs.readFile('./index.html', (err, data) => {
        res.setHeader('Content-Type', 'text/html');
        res.end(data);
      });
    } else if (method == 'GET' && url == '/api/users') {
      res.setHeader('Content-Type', 'application/json');
      res.end(JSON.stringify([{ name: 'warbler', age: 23 }]));
    }
  })
  .listen(4000, () => {
    console.log('api listen at ' + 4000);
  });
  
// proxy.js
const express = require('express')
const app = express()
app.use(express.static(__dirname + '/'))
app.listen(3000) 

// 可以同时启用两个服务器
const api = require('./http')
const proxy = require('./proxy')

// index.html
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
  (async () => {
    axios.defaults.baseURL = 'http://localhost:4000'
    const res = await axios.get("/api/users")
    console.log('data', res.data)
    document.writeln(`Response : ${JSON.stringify(res.data)}`)
  })()
</script>

当我们直接访问 http://localhost:4000/ 的时候,是可以正常取到数据的。

image.png

当我们通过 3000 端口去访问 http://localhost:4000/ 的时候,就会产生跨域错误。

image.png

通过这里也能看出来是一个跨域错误(CORS error

image.png

解决跨域问题

响应简单请求

响应简单请求:

  • 动词为 get / post / head
  • 没有自定义请求头
  • Content-Type 是 application/x-wwwform-urlencodedmultipart/form-datatext/plain 之一
    通过添加以下响应头解决:
res.setHeader("Access-Control-Allow-Origin", 'http://localhost:3000')

响应预检请求

该案例中通过添加自定义的 x-token 请求头使请求变为预检 (preflight) 请求。

// index.html
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
  (async () => {
    axios.defaults.baseURL = 'http://localhost:4000'
    const res = await axios.get("/api/users", {
      Headers: {
        "X-Token": "aaabbb"
      }
    })
    console.log('data', res.data)
    document.writeln(`Response : ${JSON.stringify(res.data)}`)
  })()
</script>

响应 preflight 请求,需要响应浏览器发出的 options 请求(预检请求),并根据情况设置响应头。

// http.js
 else if (method == 'OPTIONS') {
      res.writeHead(200, {
        "Access-Control-Allow-Origin": "http://localhost:3000",
        "Access-Control-Allow-Headers": "X-Token,Content-Type",
        "Access-Control-Allow-Methods": "PUT"
      });
      res.end();
    }

响应 credential 请求

如果要携带 cookie 信息,则请求变为 credential 请求:

// 预检options中和/users接口中均需添加
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 设置cookie
res.setHeader('Set-Cookie', 'cookie1=va222;'
// ajax服务需要设置
axios.defaults.withCredentials = true
// 服务端查看cookie 
console.log('cookie',req.headers.cookie)
// index.html
const http = require('http');
const fs = require('fs');
http.createServer((req, res) => {
  const { method, url } = req;
  if (method == 'GET' && url == '/') {
    fs.readFile('./index.html', (err, data) => {
      res.setHeader('Content-Type', 'text/html');
      res.end(data);
    });
  } else if (method == 'GET' && url == '/api/users') {
    res.setHeader('Content-Type', 'application/json');
    res.setHeader("Access-Control-Allow-Origin", 'http://localhost:3000')
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.setHeader("Set-Cookie", 'cookie1=123')
    res.end(JSON.stringify([{ name: 'warbler', age: 23 }]));
  } else if (method == 'OPTIONS') {
    res.setHeader('Access-Control-Allow-Credentials', 'true');
    res.writeHead(200, {
      "Access-Control-Allow-Origin": "http://localhost:3000",
      "Access-Control-Allow-Headers": "X-Token,Content-Type",
      "Access-Control-Allow-Methods": "PUT"
    });
    res.end();
  }
})
  .listen(4000, () => {
    console.log('api listen at ' + 4000);
  });
  
// index.html
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
  (async () => {
    axios.defaults.baseURL = 'http://localhost:4000'
    axios.defaults.withCredentials = true
    const res = await axios.get("/api/users", {
      headers: {
        "X-Token": "aaabbb"
      }
    })
    console.log('data', res.data)
    document.writeln(`Response : ${JSON.stringify(res.data)}`)
  })()
</script>

反向代理

服务端设置请求转发

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

app.use(express.static(__dirname + '/'))
app.use('/api', createProxyMiddleware({
  target: 'http://localhost:4000', changeOrigin: false
}));
app.listen(3000)

webpack devserver

vue.config.js 中配置的请求代理实际上是 webpack devserver

// vue.config.js
module.exports = {
  devServer: {
    disableHostCheck: true,
    compress: true,
    port: 5000,
    proxy: {
      '/api/': {
        target: 'http://localhost:4000',
        changeOrigin: true,
      },
    },
  }

Socket实现一个即时通讯IM

原理:Net 模块提供一个异步 API 能够创建基于流 TCP 服务器,客户端与服务器建立连接后,服务器可以获得一个全双工 Socket 对象,服务器可以保存 Socket 对象列表,在接收某客户端消息时,推送给其他客户端。

// 用于TCP通讯
const net = require("net")
// 创建服务
const chatServer = net.createServer()
// 用户列表
const clientList = []
// 监听连接事件
chatServer.on('connection', client => {
  // client => 流
  client.write("Hello\n")
  // 添加到用户列表
  clientList.push(client)
  client.on('data', data => {
    // data => 二进制通讯  Buffer
    console.log('

标签:http,跨域,nodejs,res,解决,const,data,请求
来源: https://blog.csdn.net/m0_48721669/article/details/123203207

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有