1.跨域 #
2. CORS(跨源资源共享) #
- CORS,全称为跨源资源共享 (Cross-Origin Resource Sharing),是一种允许许多现代浏览器进行跨域访问资源的机制
[ CORS 是一种官方的跨域解决方案,通过在服务器端添加一些 HTTP 头信息来告诉浏览器,允许哪些网站可以访问这些资源。在这种情况下,浏览器将不会阻止跨域请求
- 在使用 CORS 时,浏览器会在发送请求前,先发送一个预检请求(OPTIONS 请求),询问服务器是否允许当前的源进行跨源请求。如果服务器允许,那么浏览器会继续发送真正的请求。如果服务器不允许,那么浏览器会阻止这次请求
- CORS 的核心是 HTTP 头部的一组字段。主要包括:
请求头 |
说明 |
Access-Control-Allow-Origin |
指定哪些网站可以参与跨源资源共享,"*" 表示所有网站都可以 |
Access-Control-Allow-Methods |
指定了允许进行跨源请求的方法,如 GET,POST,DELETE 等 |
Access-Control-Allow-Headers |
指定了可以使用哪些 HTTP 头字段进行跨源请求 |
Access-Control-Allow-Credentials |
表示是否允许发送 Cookie |
xhr.withCredentials
是 XMLHttpRequest API 的一个属性,该属性定义了是否应在请求中包含凭据,如 Cookie 和 HTTP 认证数据
- 默认情况下,这个属性的值是 false,这意味着请求不会携带凭据。但是,如果将其设置为 true,那么请求就会包含凭据
- 然而,这并不能保证跨域请求一定会发送 Cookie。服务器也必须同意接受带有凭据的请求。服务器需要在响应头中包含以下字段才能接受带有凭据的请求
Access-Control-Allow-Origin
不能是 *
,它必须是具体的来源地址,即发送请求的网站的 URL
- xhr.withCredentials 可以使得跨域请求能够发送 Cookie 和其他凭据,但这必须得到服务器的同意。如果服务器没有正确地设置响应头,那么请求可能会因为 CORS(跨域资源共享)策略而被阻止
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('Access-Control-Allow-Origin', 'http://127.0.0.1:5500');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
let cookies = req.headers['cookie'];
console.log('Cookies: ', cookies);
res.setHeader('Set-Cookie', ['id=1', 'age=18']);
res.statusCode = 200;
res.end('hello');
});
server.listen(3000, () => console.log(`Server is listening on port 3000`));
<body>
<script>
let xhr = new XMLHttpRequest();
xhr.open('POST', 'http://127.0.0.1:3000/users', true);
xhr.withCredentials = true;
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function () {
if (xhr.status === 200) {
console.log(xhr.responseText);
}
}
xhr.send(JSON.stringify({ name: 'New User', age: 20 }));
</script>
</body>
3. JSONP #
- JSONP(JSON with Padding)是一种早期用于解决跨域数据访问限制的技术,充分利用了
script
标签的 src
属性没有跨域限制的特性
- 简单地说,JSONP 是一种通过动态插入
script
标签来调用服务器提供的 Javascript 函数返回数据的方式
- 服务器端将数据封装为一段调用某个已存在函数的 Javascript 代码(这个函数通常是在客户端定义的),然后将这个 Javascript 代码返回给客户端。客户端再去解析这个 Javascript 代码,从而获取到数据
- 这个过程通常如下:
- 客户端通过
script
标签的 src
属性发送请求给服务器,并在请求的 URL 中指定一个回调函数,如 http://localhost/jsonp?callback=myCallback
- 服务器端接收到请求后,将数据封装为这个函数的参数,然后返回这个函数的调用作为响应,如
myCallback({"name": "zhufeng", "age": 18})
- 客户端解析执行这个 Javascript 代码,也就是调用 myCallback 函数,函数参数就是需要的数据
3.1 工作原理 #
3.1.1 jsonp-server.js #
const http = require('http');
const url = require('url');
const server = http.createServer((req, res) => {
const queryObject = url.parse(req.url,true).query;
const callbackFunction = queryObject.callback;
const data = { message: 'Hello, JSONP!' };
res.writeHead(200, { 'Content-Type': 'text/javascript' });
res.end(`${callbackFunction}(${JSON.stringify(data)})`);
});
server.listen(3000, () => {
console.log('Server listening on port 3000');
});
3.1.2 jsonp-html.html #
<!DOCTYPE html>
<html>
<body>
<h1>JSONP</h1>
<button onclick="fetchData()">Fetch Data</button>
<script>
function myCallback(data) {
console.log('Received data: ', data);
}
function fetchData() {
const script = document.createElement('script');
script.src = 'http://localhost:3000?callback=myCallback';
document.body.appendChild(script);
}
</script>
</body>
</html>
3.2 百度搜索 #
3.2.1 搜索接口 #
- prod=pc:这个参数用于指定请求的设备类型,这里设置为 "pc",表示是来自电脑的请求
- wd=hello:这是关键参数,用于指定搜索的关键字。这里的 "hello" 就是你想搜索的词
- cb=jQuery1102039950249152921513_1684412625594:这个参数是回调函数的名字。这是一个 JSONP 请求,服务器会返回一个调用这个函数的 JavaScript 代码。这个函数名通常由 jQuery 自动生成
- _=1684412625601:这个参数是一个时间戳,用于防止浏览器缓存结果。每次请求的时候,jQuery 会自动生成一个新的时间戳
https:
3.2.2 jquery请求 #
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
$.ajax({
url: "https://www.baidu.com/sugrec",
jsonp: "cb",
dataType: "jsonp",
data: {
prod: "pc",
wd: "hello"
},
success: function(response) {
console.log(response);
}
});
</script>
</body>
3.2.3 封装jsonp #
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<input id="search-input" type="text" placeholder="搜索关键词">
<ul id="suggestion-list"></ul>
<script>
function jsonp({ url, jsonp, data }) {
return new Promise((resolve, reject) => {
let callbackName = `jQuery_${Date.now()}`;
window[callbackName] = function (result) {
delete window[callbackName];
document.body.removeChild(script);
resolve(result);
};
let queryStr = url.indexOf('?') === -1 ? '?' : '&';
for (let key in data) {
queryStr += `${key}=${data[key]}&`
}
let script = document.createElement('script');
script.src = `${url}${queryStr}${jsonp}=${callbackName}`;
document.body.appendChild(script);
});
}
document.getElementById('search-input').addEventListener('input', function (e) {
jsonp({
url: "https://www.baidu.com/sugrec",
jsonp: "cb",
data: {prod: "pc",wd:e.target.value}
}).then(response => {
const suggestions = response.g || [];
const suggestionList = document.getElementById('suggestion-list');
let html = '';
for (let i = 0; i < suggestions.length; i++) {
html += `<li>${suggestions[i].q}</li>`;
}
suggestionList.innerHTML = html;
});
});
</script>
</body>
</html>