跨域+四种解决方法

文章目录

本文是在学习课程满神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

javascript 复制代码
<!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

javascript 复制代码
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

javascript 复制代码
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

javascript 复制代码
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

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文件。

json 复制代码
{
  "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后端返回的结果,这表明成功实现前端代理实现跨域请求。

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

接口设置请求头

javascript 复制代码
// 返回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代理实现跨域请求的全部内容了。

相关推荐
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
寻星探路6 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
王达舒19946 小时前
HTTP vs HTTPS: 终极解析,保护你的数据究竟有多重要?
网络协议·http·https
朱皮皮呀6 小时前
HTTPS的工作过程
网络协议·http·https
Binary-Jeff6 小时前
一文读懂 HTTPS 协议及其工作流程
网络协议·web安全·http·https
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
wdfk_prog9 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习