【nodejs基础】解决跨域问题
2022/3/1 14:21:52
本文主要是介绍【nodejs基础】解决跨域问题,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
跨域问题
- 跨域:浏览器同源策略引起的接口调用问题
- 同源策略: 主机 端口 协议
- 接口调用:
XMLHttpRequest
和Fetch
都遵循同源策略 - 浏览器:浏览器发现可疑行为,拒绝接收
浏览器限制跨域请求一般有两种方式:
- 浏览器限制发起跨域请求
- 跨域请求可以正常发起,但是返回的结果被浏览器拦截了
一般浏览器都是第二种方式限制跨域请求,那就是说请求已到达服务器,并有可能对数据库里的数据进行了操作,但是返回的结果被浏览器拦截了,那么我们就获取不到返回结果,这是一次失败的请求,但是可能对数据库里的数据产生了影响。
为了防止这种情况的发生,规范要求,对这种可能对服务器数据产生副作用的 HTTP
请求方法,浏览器必须先使用 OPTIONS
方法发起一个预检请求,从而获知服务器是否允许该跨域请求:如果允许,就发送带数据的真实请求;如果不允许,则阻止发送带数据的真实请求。
把这个选项勾上就可以看到预检请求了,关于预检请求,可以参看下面文章。
预检请求
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/
的时候,是可以正常取到数据的。
当我们通过 3000
端口去访问 http://localhost:4000/
的时候,就会产生跨域错误。
通过这里也能看出来是一个跨域错误(CORS error
)
解决跨域问题
响应简单请求
响应简单请求:
- 动词为
get
/post
/head
- 没有自定义请求头
- Content-Type 是
application/x-wwwform-urlencoded
,multipart/form-data
或text/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('
这篇关于【nodejs基础】解决跨域问题的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2025-01-04React 19 来了!新的编译器简直太棒了!
- 2025-01-032025年Node.js与PHP大比拼:挑选最适合的后端技术进行现代web开发
- 2025-01-03?? 用 Gemini API、Next.js 和 TailwindCSS 快速搭建 AI 推文生成项目 ??
- 2024-12-31Vue CLI多环境配置学习入门
- 2024-12-31Vue CLI学习入门:一步一步搭建你的第一个Vue项目
- 2024-12-31Vue3公共组件学习入门:从零开始搭建实用组件库
- 2024-12-31Vue3公共组件学习入门教程
- 2024-12-31Vue3学习入门:新手必读教程
- 2024-12-31Vue3学习入门:初学者必备指南
- 2024-12-30Vue CLI多环境配置教程:轻松入门指南