简单介绍跨域问题
其他相关内容:
跨域 - JSONP
跨域 - CORS跨域资源共享
- 跨域问题只在浏览器中存在,因为在浏览器中才有是否同源的问题。
- 前端配置代理,通过代理在客户端和服务器之间通信,可以解决跨域问题。
1,同源策略
在浏览器中,同源策略(Same-origin_policy)是一个重要的安全策略。用于限制不同源之间的资源交互。
源的定义:如果两个 URL 的协议、端口、主机都相同,则这两个 URL 是同源的。
以 https://blog.csdn.net/pc/index.html
为例:
URL | 是否同源 | 原因 |
---|---|---|
https://blog.csdn.net/h5 /index.html | √ | 只有路径不同 |
https://blog.csdn.net/pc/qq /index.html | √ | 只有路径不同 |
http: //blog.csdn.net/pc/index.html | × | 协议不同 |
https://blog.csdn.net:8081 /pc/index.html | × | 端口不同(https 默认 443) |
https://edu .csdn.net/pc/index.html | × | 主机不同 |
1.1,跨域资源共享CORS
简单来说,是一种基于 HTTP 头的机制,该机制通过允许服务器指定允许浏览器访问的源来加载自己的资源。
常见的是在服务端接口处添加响应头:Access-Control-Allow-Origin
来设置允许访问该接口的源。
// 允许任意源访问
ctx.set("Access-Control-Allow-Origin", "*");
2,跨域问题
不同源之间的资源交互,就会存在跨域问题。
最常遇到:请求的接口不同源导致跨域。
2.1,formData 和 form 表单提交
- 通过 formData 创建的数据,需要发送HTTP请求来提交数据。虽然代码简洁,但可能会跨域。
- 通过 form 表单提交,不会跨域。
测试代码:
<script setup lang="ts">
import { ref, nextTick } from "vue";
import type { Ref } from "vue";
const sendForm= () => {
var formData = new FormData();
formData.append("name", "下雪天的夏风");
formData.append("want", "求关注");
var request = new XMLHttpRequest();
request.open("POST", "http://127.0.0.1:3000/post");
request.send(formData);
};
const _form = ref() as Ref<HTMLFormElement>;
const formInfo = ref({});
const submitForm= async () => {
formInfo.value = {
name: "下雪天的夏风",
want: "求关注",
};
await nextTick();
_form.value.submit();
};
</script>
<template>
<button @click="submitForm">form 表单提交</button>
<button @click="sendForm">formData 发送请求</button>
<form ref="_form" action="http://127.0.0.1:3000/post" method="post">
<input v-for="(value, key) in formInfo" :name="key" :value="value" :key="key" type="hidden" />
</form>
</template>
3,解决跨域
3.1,后端配置响应头
以 Koa
框架为例,简单示例app.js
。
这只是简单请求的举例,更详细的内容参考 跨域 - CORS跨域资源共享
const Koa = require("koa");
const Router = require("koa-router");
const { bodyParser } = require("@koa/bodyparser");
const app = new Koa();
const router = new Router();
router.post("/post", (ctx) => {
//设置响应头允许跨域
ctx.set("Access-Control-Allow-Origin", "http://localhost:4001");
ctx.body = {
code: 1,
postParams: ctx.request.body,
};
});
app.use(bodyParser()).use(router.routes());
app.listen(3000);
或对所有请求都配置
// 允许的源
const isAllowedOrigin = (origin) => {
const allowedOrigins = ['http://localhost:4000', 'http://localhost:5173'];
return allowedOrigins.includes(origin);
};
router.all("/(.*)", (ctx, next) => {
const { origin } = ctx.headers;
if (!isAllowedOrigin(origin)) {
return;
}
ctx.set("Access-Control-Allow-Origin", origin);
next();
});
前端发送请求
constrequest = new XMLHttpRequest();
request.open("POST", "http://127.0.0.1:3000/post");
request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
request.send("foo=bar&lorem=ipsum");
3.2,前端配置代理
本地开发时,前端联调后端接口会使用到代理来解决跨域问题。
1,原理
代理服务器转发客户端(浏览器)的请求到服务端,并将结果返回给客户端。
这样,浏览器就不会向服务器直接发起跨域请求了。
2,配置代理举例
webpac 的 webpack-dev-server插件,参考
module.exports = {
//...
devServer: {
proxy: {
'/api': {
target: '"http://localhost:3000',
pathRewrite: {'^/api' : ''},
changeOrigin: true, // target是域名的话,需要这个参数,
secure: false, // 设置支持 https协议的代理
},
'/api2': {
.....
}
}
}
};
vite,参考
// vite.config.js
import { defineConfig } from "vite";
export default defineConfig({
server: {
proxy: {
"/api": {
target: "http://localhost:3000",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ""),
},
},
},
});
以上。