跨域+四种解决方法

news2024/11/13 22:24:37

文章目录

    • 一、跨域
    • 二、JSONP实现跨域请求
    • 三、前端代理实现跨域请求
    • 四、后端设置请求头实现跨域请求
    • 五、Nginx代理实现跨域请求
      • 5.1 安装Nginx软件
      • 5.2 使用Ubuntu安装nginx

本文是在学习课程满神yyds后记录的笔记,强烈推荐读者去看此课程。

一、跨域

出于浏览器的同源策略限制,浏览器会拒绝跨域请求。

什么是同源策略

请求的时候拥有相同的协议、域名、端口,只要有一个不同就属于跨域

主机(http://www.small5.com)是否跨域原因
https://www.small5.com协议不同 http和https
http://www.small5.com:8001端口不同 80 和 8001
http://www.baidu.com域名不同 small5和百度
http://www.small5.com/index.html协议、端口、域名全部相同

跨域的四种解决方案:

  1. 前后端协商jsoup
  2. 前端解决 使用代理dev
  3. 后段解决 设置请求头
  4. 运维端解决 nginx代理

二、JSONP实现跨域请求

jsonp解决跨域的原理是:由于script标签的src属性不受同源策略的限制,可以跨域请求到服务器端的资源,并且把资源作为脚本(js)来运行,因此可以解决跨域。

img属性也有src属性,但是它只是把服务器的资源返回,并没有进一步做任何js的动作。

但是jsonp解决跨域存在一个限制:它只能发送get请求

JSONP解决跨域的基本工作原理

  1. 前端定义一个回调函数,该函数将处理从服务器返回的数据
  2. 前端创建一个script标签,并将跨域请求的url作为script标签的src属性值,在url中,需要包含一个特殊的参数,该参数用来告知服务器回调函数的名称,后端服务器会通过query拿到回调函数名。
  3. 服务器接收到请求后,解析url参数,通过query拿到回调函数名,并响应请求:将要发送的数据作为参数传入回调函数返回前端【使用模板字符串调用前端函数】
  4. 前端接收到响应后,由于脚本标签生成的script元素的src属性已经指向了服务器返回的脚本url,浏览器会自动执行该脚本,从而调用前端所定义的回调函数,并将服务器返回的数据作为参数传入。

可以看出使用JSONP解决跨域需要前后端相互配合实现

下面我们通过代码实现JSONP实现跨域访问:
前端html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jsonp实现跨域</title>
</head>

<body>
    <script>
        // jsonp函数实现了跨域访问,,其中包括1.定义script并设置src传递回调函数名 2.前端window中动态定义回调函数
        // name表示回调函数名
        const jsonp = (name) => {
            // 2.前端创建一个script标签,并将跨域请求的url作为script标签的src属性值,
            // 在url中,需要包含一个特殊的参数,该参数用来告知服务器回调函数的名称,后端服务器通过query拿到回调函数名
            let script = document.createElement('script')
            script.src = 'http://localhost:3000/api/jsonp?callback=' + name  // 将回调函数名传递给服务器
            document.body.appendChild(script)
            return new Promise((resolve) => {
                // 1.前端动态定义一个回调函数,该函数将处理从服务器返回的数据
                window[name] = (data) => {  // data是后端调用前端回调函数返回的数据
                    // 4. 返回服务器返回的数据
                    resolve(data)
                }
                console.log(window);
            })
        }
        // 调用jsonp方法
        // 这里的回调函数名为callback加时间戳 使用时间戳的原因是避免函数重名
        jsonp(`callback${new Date().getTime()}`).then(res => {
            // 输出服务器返回的数据
            console.log(res);
        })
    </script>
</body>

</html>

后端使用node,index.js

const express = require('express')

const app = express()
// 发送get请求
app.get('/api/jsonp', (req, res) => {
    // 3. 服务器接收到请求后,解析url参数,通过query拿到回调函数名,并发送请求:将要发送的数据作为参数传入回调函数
    const { callback } = req.query
    // 后端响应请求并返回数据
    // 注意这里以模板字符串的形式返回数据,实现调用前端的回调函数,传递了值为hello small5的参数,并执行这个前端回调函数
    // 实际上这个参数的内容才是真实响应数据,改变参数内容,前端收到的数据也随之改变
    res.send(`${callback}('hello small5')`)
})

app.listen(3000, () => {
    console.log('server is running');
})

运行端口为3000的服务器
在这里插入图片描述
通过Live Server运行html文件,Live Server会自动开一个5500的端口,这和服务器端口不一致,因此是存在跨域的。
在这里插入图片描述
通过上图,可以看到使用JSONP实现跨域请求的请求结果,状态码是200,表示请求成功并响应结果,这表明使用JSONP成功实现了跨域请求。

下面我们来看看请求传递的参数和响应结果以验证:
在这里插入图片描述
在这里插入图片描述
可以看到前端成功传递参数值为callback+时间戳的回调函数名、参数名为callback的参数,以及响应结果是调用此回调函数并传参。

下面我们再看看前端输出的请求结果:
在这里插入图片描述
可以看到前端成功接收响应的数据,下面我们通过输出的window来验证是否有一个名为callback+时间戳的回调函数
在这里插入图片描述
可以看到完全符合预期,前端确实存在名为callback+时间戳的回调函数。

以上就是JSONP实现跨域请求的所有内容。

三、前端代理实现跨域请求

前端使用代理来实现跨域请求需要借助前端构建工具,如webpack、rollup、vite之类构建工具,通过在配置文件中配置代理,实现拦截请求并转发到指定服务器上。

这里我以vite来举例:

使用npm i vite -D安装vite,并在根目录新建vite.config.js文件。
vite.config,js

import { defineConfig } from 'vite'

export default defineConfig({
    server: {
        proxy: {
            '/api': {
                target: 'http://localhost:3000',
                changeOrigin: true,  // changeOrigin表示是否改变请求的源
                // rewrite: (path)=>path.replace(/^\/api/, '')  // rewrite表示重写请求路径,这里的示例实现了匹配到/api/则替换为空
            }
        }
    }
})

node配置服务器:index.js

const express = require('express')

const app = express()

// 返回json数据的get请求
app.get('/api/json', (req, res) => {
    res.json({name: 'small5'})
})

app.listen(3000, () => {
    console.log('server is running');
})

html文件发送请求:index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jsonp解决跨域</title>
</head>

<body>
    <script>
        // Fetch 是一个基于 promise 的用于浏览器和 node.js 的 HTTP 客户端,
        // 它能够轻松的将请求发送到服务器并得到响应。
        // Fetch 使用方式简单,又能充分发挥出 Promise 的特性,是越来越多人使用的发起请求的方式之一。
        fetch('/api/json').then(res => res.json()).then(res => {
            console.log(res);
        })
    </script>
</body>

</html>

这里我们就不能使用Live Server运行html文件了,需要配置一下pacage.json文件,使用vite运行html文件。

{
  "scripts": {
    "dev": "vite"
  },
  "dependencies": {
    "express": "^4.17.1",
    "express-session": "^1.17.3",
  },
  "devDependencies": {
    "vite": "^4.4.8"
  }
}

现在我们使用npm run dev命令运行:
在这里插入图片描述
可以看到vite自动开了url为http://127.0.0.1:5173客户端域名,这和node后端定义的http://localhost:3000是存在跨域的。查看请求结果如下:
在这里插入图片描述
可以看到状态码200,表示成功实现了跨域请求,再看看接口响应结果和前端得到的请求结果:
在这里插入图片描述

在这里插入图片描述
成功得到node后端返回的结果,这表明成功实现前端代理实现跨域请求。

四、后端设置请求头实现跨域请求

接口设置请求头

// 返回json数据的get请求
app.get('/api/json', (req, res) => {
    // res.setHeader('Access-Control-Allow-Origin', '*') 任何请求都能跨域 不安全
    res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500')  // 指定某个请求能跨域
    res.json({name: 'small5'})
})

五、Nginx代理实现跨域请求

由于本人对于Ubuntu不太了解,因此这里主要讲解一下使用Ubuntu安装nginx并实现跨域请求

5.1 安装Nginx软件

安装完成后在nginx,conf配置代理
在这里插入图片描述

5.2 使用Ubuntu安装nginx

在Microsoft Store中安装对应的Ubuntu,我安装的是以下版本。
在这里插入图片描述

下载完成后启动Ubuntu,可能会报错解决方式参考:Ubuntu 20.04.4 LTS 报错解决方案。

Ubuntu正常启动后输入apt-get install nginx安装nginx,如果报没有权限的错误,解决这个问题,需要以root用户身份或具有sudo权限的用户身份运行命令。可以尝试在命令前加sudo
在这里插入图片描述
安装完成后再次输入命令,验证安装结果,得到如下结果表示安装成功

在这里插入图片描述
接着我们打开资源管理器,找到Linux目录,安装的nginx默认是在ect文件夹下,
在这里插入图片描述
nginx的默认配置文件是在sites-avaliable文件夹下
在这里插入图片描述
下面我们打开默认配置文件找到默认服务配置的server配置一个代理。
在这里插入图片描述
注意,使用nginx配置代理不能使用localhost,必须使用Windows主机的ip,打开Ubuntu输入cat /etc/resolv.conf可以获取到主机ip。
在这里插入图片描述
我们可以验证一下使用Windows主机ip能否请求成功接口。

下面我们配置好后端node接口并启动服务器如下图:
在这里插入图片描述
在Ubuntu上使用命令curl http://主机IP:服务器端口号/接口地址发送请求。

想了解curl命令可以查看博客:技术分享 | 使用 cURL 发送请求
在这里插入图片描述
可以看到返回正确结果,接口发送成功,这表明使用Windows主机ip作为代理域名是没有问题的。

验证Windows主机ip地址作为代理域名没有问题后,我们再使用Windows主机ip在nginx配置文件中配置代理如下:
在这里插入图片描述
这里我遇到一个问题,不管是使用记事本还是vscode打开配置文件,再修改编辑后保存会无法保存,尝试了好几次都保存不了,后面我是通过在Ubuntu的Linux系统中使用vim编辑器打开配置文件,然后修改保存,这其中又遇到一个问题,如果没有加sudo权限的话执行vim 配置文件路径打开后修改文件保存会报此文件是只读文件,不允许修改,因此要在指令前加sudo,提供sudo权限后选择输入E,进入编辑,修改后保存可以看到配置文件成功被修改了。

关于vim编辑器的基本使用可以查看我的博客git的基本使用中的预备知识那节内容。

在这里插入图片描述
接着我们在Ubuntu中输入nginx启动nginx,我这里没有权限,添加了sudo后才启动成功,另外由于配置文件的location拼写错误还报错了一次。如下:
在这里插入图片描述
接着在浏览器地址栏输入localhost:80回车,得到如下页面表示nginx启动成功
在这里插入图片描述
下面就不再nginx写代码了,直接在开发者工具内发送请求验证是否实现跨域请求是一样的。
在这里插入图片描述
通过查看请求状态码可以验证成功实现了跨域请求
在这里插入图片描述

使用nginx代理实现跨域适合项目上线时使用,因此这种方式使用非常普遍。

以上就是使用nginx代理实现跨域请求的全部内容了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/840035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【力扣】23. 合并 K 个升序链表 <链表指针、堆排序、分治>

目录 【力扣】23. 合并 K 个升序链表题解方法一&#xff1a;暴力&#xff0c;先遍历取出来值到数组中排序&#xff0c;再生成新链表方法二&#xff1a;基础堆排序&#xff08;使用优先队列 PriorityQueue&#xff09;方法三&#xff1a;基础堆排序&#xff08;使用优先队列 Pri…

时序预测 | Matlab实现基于SVR支持向量机回归的电力负荷预测模型

文章目录 预测结果基本介绍程序设计参考资料预测结果 基本介绍 时序预测 | Matlab实现基于SVR支持向量机回归的电力负荷预测模型 支持向量机(英语:support vector machine,常简称为SVM,又名支持向量网络)是在分类与回归分析中分析数据的监督式学习模型与相关的学习算法。给…

微信小程序nodejs+vue+uniapp个人家庭理财系统--论文

随着Internet的发展&#xff0c;人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化&#xff0c;网络化和电子化。网上管理&#xff0c;它将是直接管理家庭理财系统app的最新形式。本论文是以构建家庭理财系统app为目标&#xff0c;使用nodejs技术制作&…

什么是Linux,如何在Windows操作系统下搭建Linux环境,远程连接Linux系统

文章目录 什么是LinuxLinux的诞生及发展为什么要学习LinuxLinux内核Linux发行版什么是虚拟机如何在VMware虚拟机中搭建Linux系统环境远程连接 Linux 系统Linux 帮助网站 什么是Linux Linux是一套免费使用和自由传播的类Unix操作系统&#xff0c;是一个基于POSIX和UNIX的多用户…

概念解析 | PointNet概述

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次解析的概念是:点云深度学习及PointNet论文概述 参考论文:Qi C R, Su H, Mo K, et al. Pointnet: Deep learning on point sets for 3d classification and segmentation[C]//Proceedings of …

《向量数据库指南》——GPTCache 中的温度参数

目录 GPTCache 中的温度参数 a. 从多个候选答案中随机选择 b. 调整概率跳过缓存,直接调用模型 GPTCache 中的温度参数 为了平衡响应的随机性和一致性,并满足用户偏好或应用需求,在多模态 AI 应用中选择适当的温度参数值至关重要。GPTCache 保留了机器学习中温度参数的概…

Netty 粘包半包

什么是 TCP 粘包半包&#xff1f; 假设客户端分别发送了两个数据包 D1 和 D2 给服务端&#xff0c;由于服务端一次读取到的字节 数是不确定的&#xff0c;故可能存在以下 4 种情况。 &#xff08;1&#xff09;服务端分两次读取到了两个独立的数据包&#xff0c;分别…

kubernetes基于helm部署gitlab-operator

kubernetes基于helm部署gitlab-operator 这篇博文介绍如何在 Kubernetes 中使用helm部署 GitLab-operator。 先决条件 已运行的 Kubernetes 集群负载均衡器&#xff0c;为ingress-nginx控制器提供EXTERNAL-IP&#xff0c;本示例使用metallb默认存储类&#xff0c;为gitlab p…

ChatGPT“侵入”校园,教学评价体制受冲击,需作出调整

北密歇根大学的教授奥曼在学生作业中发现了一篇关于世界宗教的“完美论文”。“这篇文章写得比大多数学生都要好......好到不符合我对学生的预期&#xff01;”他去问ChatGPT&#xff1a;“这是你写的吗&#xff1f;”ChatGPT回答&#xff1a;“99.9%的概率是的。” ChatGPT“侵…

C++二叉搜索树剖析

目录 &#x1f347;二叉搜索树概念&#x1f348;二叉搜索树查找&#x1f349;二叉搜索树的插入&#x1f34a;二叉搜索树的删除&#x1f34d;二叉搜索树的查找、插入、删除实现&#x1f34b;二叉搜索树的应用&#x1f96d;二叉搜索树的性能分析&#x1f353;总结 &#x1f347;二…

Mac 创建和删除 Automator 工作流程,设置 Terminal 快捷键

1. 创建 Automator 流程 本文以创建一个快捷键启动 Terminal 的自动操作为示例。 点击打开 自动操作&#xff1b; 点击 新建文稿 点击 快速操作 选择 运行 AppleScript 填入以下内容 保存名为 “Open Terminal” 打开 设置 > 键盘&#xff0c;选择 键盘快捷键 以此选择 服…

Python(六十九)为什么要将元组设计成不可变序列

❤️ 专栏简介&#xff1a;本专栏记录了我个人从零开始学习Python编程的过程。在这个专栏中&#xff0c;我将分享我在学习Python的过程中的学习笔记、学习路线以及各个知识点。 ☀️ 专栏适用人群 &#xff1a;本专栏适用于希望学习Python编程的初学者和有一定编程基础的人。无…

git bash 安装sdkadmin

1.下载相关安装包,复制到git 安装目录 D:\software\Git\mingw64\bin 2. 运行 curl -s "https://get.sdkman.io" | bash

心跳跟随的心形灯(STM32(HAL)+WS2812+MAX30102)

文章目录 前言介绍系统框架原项目地址本项目开发开源地址硬件PCB软件功能 详细内容硬件外壳制作WS2812级联及控制MAX30102血氧传感器0.96OLEDFreeRTOS 效果视频总结 前言 在好几年前&#xff0c;我好像就看到了焊武帝 jiripraus在纪念结婚五周年时&#xff0c;制作的一个心跳跟…

10 日志系统(下)

10 日志系统&#xff08;下&#xff09; 本文内容 日志系统分为两部分&#xff0c;其一是单例模式与阻塞队列的定义&#xff0c;其二是日志类的定义与使用。 本篇将介绍日志类的定义与使用&#xff0c;具体的涉及到基础API&#xff0c;流程图与日志类定义&#xff0c;功能实现…

Android 刷新与显示

目录 屏幕显示原理&#xff1a; 显示刷新的过程 VSYNC机制具体实现 小结&#xff1a; 屏幕显示原理&#xff1a; 过程描述&#xff1a; 应用向系统服务申请buffer 系统服务返回一个buffer给应用 应用开始绘制&#xff0c;绘制完成就提交buffer&#xff0c;系统服务把buffer数据…

第三章 CUDA编译器环境配置篇

cuda教程目录 第一章 指针篇 第二章 CUDA原理篇 第三章 CUDA编译器环境配置篇 第四章 kernel函数基础篇 第五章 kernel索引(index)篇 第六章 kenel矩阵计算实战篇 第七章 kenel实战强化篇 第八章 CUDA内存应用与性能优化篇 第九章 CUDA原子(atomic)实战篇 第十章 CUDA流(strea…

Fatal error, can‘t open config file ‘/myredis/redis.conf‘: No such file or directory

在学习Redis到主从复制部分&#xff0c;进行相关练习&#xff0c;基本过程如下 1.首先将redis.conf文件cp到自建myredis文件夹中&#xff0c;并配置不同端口号的redis.conf redisXXXX.confd的配置内容如下 &#xff1a; include /myredis/redis.conf pidfile /var/run/redis_…

压力测试与测试工具jmeter的介绍

目录 一、性能指标 二、jmeter &#xff08;一&#xff09;JMeter 安装 &#xff08;二&#xff09;JMeter 压测示例 1、添加线程组 2、添加 HTTP 请求 3、添加监听器 4、启动压测&查看分析结果 &#xff08;三&#xff09;JMeter Address Already in use 错误解决 压力测…

Flutter运行app时向logcat输出当前打开的界面路径且点击可跳转

当一个项目大了目录文件多了&#xff0c;我们往往会为了找到一个文件花费大量的时间和精力&#xff0c;为了快捷方便的调试我们的项目&#xff0c;我们往往需要在打开app运行的时候需要知道当前打开的界面的文件在哪儿&#xff0c;我们这个代码就能快捷的知道我们app正在打开的…