Ajax 跨域 一起来吧!

Ajax 简介

Ajax读音:[ˈeɪdʒæks]

Ajax 的全程:Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
Ajax 不是某种编程语言,是一种在无需重新加载整个页面的情况之下能够更新网页部分的技术。

解决问题

传统的网页(不使用 Ajax 技术的页面),想要更新内容或者提交一个表单,就需要重新载入页面。
使用了 Ajax 技术的页面,通过在后台跟服务器进行少量的数据交互,网页就可以实现异步局部更新。

Ajax 概念

同步和异步

Ajax 未出现时,网页和服务器的交互

同步
异步

Ajax 技术的实现得益于一个对象的出现 XMLHttpRequest 的出现

运用 HTML 和 CSS 来实现页面,表达信息;
运用 XMLHttpRequest 和 web 服务器进行数据异步交互
运用 JavaScript 操作 DOM,实现动态局部刷新

XMLHttpRequest 对象使用

1
let xmlhttp = new XMLHttpRequest();

IE5、IE6 不支持 XMLHttpRequest 对象。
兼容IE5、IE6写法

1
2
3
4
5
6
7
8
9
let xmlhttp;

if (window.XMLHttpRequest) {
// Support: IE 9-11
xmlhttp = new XMLHttpRequest();
} else {
// Support: IE6-IE8
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

HTTP 请求

什么是 HTTP

HTTP 是计算机通过网络进行通信的规则。主要应用在 web领域。 HTTP 是一种无状态协议,服务端不保留连接的相关信息。没有记忆的。 HTTP 是明文传输。

完整的 HTTP 请求 过程
1、建立 TCP 连接
2、Web 浏览器向 Web服务器发送请求命令,发送请求头信息
3、Web 服务器应答,服务器发送应答头信息,服务器向浏览器发送数据
4、Web 服务器关闭 TCP 连接。

HTTP 请求组成

1、HTTP 请求的方法或动作 比如是 GET 还是 POST 请求
2、请求的 URL地址。
3、请求头,包含一些客户端环境信息,身份验证等。
4、请求主体,也就是请求正文,请求正文中可以包含客户提交的查询字符串信息,表单信息等等。

HTTP请求

HTTP 请求

GET:一般用于信息获取,使用 URL 传递参数,对所发送信息的数量也有限制,一般在 2000 个字符
POST:一般用于修改服务器上的资源。对所发信息的数量无限制。相对于GET请求安全。

HTTP 响应组成

1、一个数字和文字组成的状态码,用来显示请求是否成功
2、响应头,响应头也是和请求头一样包含许多有用的信息,例如服务器类型、日期时间、内容类型和长度等。
3、响应主体,也就是响应正文。

HTTP响应

HTTP 请求
HTTP 状态码由 3 位数字构成,其中首位数字定义了状态码的类型:
1XX:信息类,表示收到 Web 浏览器请求,正在进一步的处理中
2XX:成功,表示用户请求被正确接受,理解和处理例如:200 OK
3XX:重定向,表示请求没有成功,客户必须采取进一步的动作
4XX:客户端错误,表示客户端提交的请求有错误,例如:404 NOT Found,意味着请求中所引用的文档不存在
5XX:服务器错误,表示服务器不能完成对请求的处理:如 500

XMLHttpRequest 发送请求

open(method,url,async)
send(string)

例子:

例子

XMLHttpRequest 取得响应

responseText:获得字符串形式的响应数据
responseXML:获得 XML 形式的响应数据
status 和 statusText:以数字和文本形式返回 HTTP 状态码
getAllResponseHeaders():获取所有的响应头
getResponseHeader():查询响应中的某个字段的值
readyState 属性
0:请求未初始化,open 还没有调用
1:服务器连接已建立,open 已经调用了
2:请求已接收,也就是接收到头信息了
3:请求处理中,也就是接收到响应主体了
4:请求已完成,且响应已就绪,也就是响应完成了

示例代码:

1
2
3
4
5
6
7
8
9
10
11
let xmlhttp = new XMLHttpRequest();

// Support: IE 9-11

xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
// 做一些事情 xml.responseText
}
};
xmlhttp.open("GET", "/try/ajax/ajax_info.txt");
xmlhttp.send();

Ajax 的简单例子

例子简介

1、查询员工信息,可以通过输入员工编号查询员工基本信息;
2、新建员工信息,包含员工姓名、员工编号、员工性别、员工职位;

纯 html 页面,用来实现员工查询和新建的页面;
php 页面,用来实现查询员工和新建员工的后台接口;

PHP

PHP 是一种创建动态交互性站点的服务器端脚本语言
PHP 能够生成动态页面内容
PHP 能够创建、打开、读写、删除以及关闭服务器上的文件
PHP 能够接收表单数据
PHP 能够发送并取回 cookie
PHP 能够添加、删除、修改数据库中的数据
PHP 能够限制用户访问网站中的某些页面
……

php 入门简单、免费、开源、应用广泛

涉及提到点
wordpress 博客系统
XAMPP (服务集合)
Fiddler 工具(抓包工具)

我这里使用的是 phpstudy 、 Postman 来测试接口。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
document.getElementById("search").onclick = function() {
var request = new XMLHttpRequest();
request.open("GET", "server.php?number=" + document.getElementById("keyword").value);
request.send();
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
document.getElementById("searchResult").innerHTML = request.responseText;
} else {
alert("发生错误:" + request.status);
}
}
}
}

document.getElementById("save").onclick = function() {
var request = new XMLHttpRequest();
request.open("POST", "server.php");
var data = "name=" + document.getElementById("staffName").value
+ "&number=" + document.getElementById("staffNumber").value
+ "&sex=" + document.getElementById("staffSex").value
+ "&job=" + document.getElementById("staffJob").value;
request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
document.getElementById("createResult").innerHTML = request.responseText;
} else {
alert("发生错误:" + request.status);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
//设置页面内容是html编码格式是utf-8
header("Content-Type: text/plain;charset=utf-8");
//header("Content-Type: application/json;charset=utf-8");
//header("Content-Type: text/xml;charset=utf-8");
//header("Content-Type: text/html;charset=utf-8");
//header("Content-Type: application/javascript;charset=utf-8");

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
(
array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
);

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
create();
}

//通过员工编号搜索员工
function search(){
//检查是否有员工编号的参数
//isset检测变量是否设置;empty判断值为否为空
//超全局变量 $_GET 和 $_POST 用于收集表单数据
if (!isset($_GET["number"]) || empty($_GET["number"])) {
echo "参数错误";
return;
}
//函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
//global 关键词用于访问函数内的全局变量
global $staff;
//获取number参数
$number = $_GET["number"];
$result = "没有找到员工。";

//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
if ($value["number"] == $number) {
$result = "找到员工:员工编号:" . $value["number"] . ",员工姓名:" . $value["name"] .
",员工性别:" . $value["sex"] . ",员工职位:" . $value["job"];
break;
}
}
echo $result;
}

//创建员工
function create(){
//判断信息是否填写完全
if (!isset($_POST["name"]) || empty($_POST["name"])
|| !isset($_POST["number"]) || empty($_POST["number"])
|| !isset($_POST["sex"]) || empty($_POST["sex"])
|| !isset($_POST["job"]) || empty($_POST["job"])) {
echo "参数错误,员工信息填写不全";
return;
}
//TODO: 获取POST表单数据并保存到数据库

//提示保存成功
echo "员工:" . $_POST["name"] . " 信息保存成功!";
}

源码

JSON 格式

JSON 基本概念

JSON:JavaScript 对象表示法(JavaScript Object Notation)
JSON 是存储和交换文本信息的语法,类似 XML。它采用键值对的方式来组织,易于人们阅读和编写,同时也易于机器解析和生产
JSON 是独立于语言的,也就是说不管什么语言,都可以解析 json,只需要按照 json 的规则来就行。

JSON 与 XML 比较

json 的长度 和 xml 格式比起来很短小
json 读写的速度更快
json 可以使用 JavaScript 内建的方法直接进行解析,转成为 JavaScript 对象,非常方便。

JSON 语法规则

JSON 数据的书写格式是:名称/值对
名称/值对组合中的名称写在前面(在双引号中),值对写在后面(同样在双引号中),中间用冒号隔开:比如 “name”:“郭靖”

json 的值可以是下面这些类型
数字(整数或浮点数),比如123,1.23
字符串(在双引号中)
逻辑值(true 或 false)
数组 (在方括号中)
对象 (在花括号中)
null

JSON 解析

eval 和 JSON.parse
一般使用 JSON.parse,使用 eval 很危险。

JSON 检验工具
JSONLint

采用 json 格式
代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
document.getElementById("search").onclick = function() { 
var request = new XMLHttpRequest();
request.open("GET", "serverjson.php?number=" + document.getElementById("keyword").value);
request.send();
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
var data = JSON.parse(request.responseText);
if (data.success) {
document.getElementById("searchResult").innerHTML = data.msg;
} else {
document.getElementById("searchResult").innerHTML = "出现错误:" + data.msg;
}
} else {
alert("发生错误:" + request.status);
}
}
}
}

document.getElementById("save").onclick = function() {
var request = new XMLHttpRequest();
request.open("POST", "serverjson.php");
var data = "name=" + document.getElementById("staffName").value
+ "&number=" + document.getElementById("staffNumber").value
+ "&sex=" + document.getElementById("staffSex").value
+ "&job=" + document.getElementById("staffJob").value;
request.setRequestHeader("Content-type","application/x-www-form-urlencoded");
request.send(data);
request.onreadystatechange = function() {
if (request.readyState===4) {
if (request.status===200) {
var data = JSON.parse(request.responseText);
if (data.success) {
document.getElementById("createResult").innerHTML = data.msg;
} else {
document.getElementById("createResult").innerHTML = "出现错误:" + data.msg;
}
} else {
alert("发生错误:" + request.status);
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
<?php
//设置页面内容是html编码格式是utf-8
header("Content-Type: text/plain;charset=utf-8");
//header("Content-Type: application/json;charset=utf-8");
//header("Content-Type: text/xml;charset=utf-8");
//header("Content-Type: text/html;charset=utf-8");
//header("Content-Type: application/javascript;charset=utf-8");

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
(
array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
);

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
create();
}

//通过员工编号搜索员工
function search(){
//检查是否有员工编号的参数
//isset检测变量是否设置;empty判断值为否为空
//超全局变量 $_GET 和 $_POST 用于收集表单数据
if (!isset($_GET["number"]) || empty($_GET["number"])) {
echo '{"success":false,"msg":"参数错误"}';
return;
}
//函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
//global 关键词用于访问函数内的全局变量
global $staff;
//获取number参数
$number = $_GET["number"];
$result = '{"success":false,"msg":"没有找到员工。"}';

//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
if ($value["number"] == $number) {
$result = '{"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
',员工姓名:' . $value["name"] .
',员工性别:' . $value["sex"] .
',员工职位:' . $value["job"] . '"}';
break;
}
}
echo $result;
}

//创建员工
function create(){
//判断信息是否填写完全
if (!isset($_POST["name"]) || empty($_POST["name"])
|| !isset($_POST["number"]) || empty($_POST["number"])
|| !isset($_POST["sex"]) || empty($_POST["sex"])
|| !isset($_POST["job"]) || empty($_POST["job"])) {
echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
return;
}
//TODO: 获取POST表单数据并保存到数据库

//提示保存成功
echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}

?>

源码

jQuery 中的 Ajax

jQuery.ajax([settings])

type:类型,“POST” 或 “GET”,默认为 “GET”
url:发送请求的地址
data:是一个对象,连同请求发送到服务器的数据
dataType:预期服务器返回的数据类型。如果不指定,jQuery 将自动根据 HTTP 包 MIME 信息来智能判断,一般采用 json 格式,可以设置为“json”
success:是一个方法,请求成功后的回调函数。传入返回后的数据,以及包含成功代码的字符串
error:是一个方法,请求失败后调用此函数。传入 XMLHttpRequest 对象

静态资源库
360 前端静态资源库
腾讯前端静态资源库
字节跳动 前端静态资源库

改写代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
$(document).ready(function(){ 
$("#search").click(function(){
$.ajax({
type: "GET",
url: "serverjson2.php?number=" + $("#keyword").val(),
dataType: "json",
success: function(data) {
if (data.success) {
$("#searchResult").html(data.msg);
} else {
$("#searchResult").html("出现错误:" + data.msg);
}
},
error: function(jqXHR){
alert("发生错误:" + jqXHR.status);
},
});
});

$("#save").click(function(){
$.ajax({
type: "POST",
url: "serverjson2.php",
data: {
name: $("#staffName").val(),
number: $("#staffNumber").val(),
sex: $("#staffSex").val(),
job: $("#staffJob").val()
},
dataType: "json",
success: function(data){
if (data.success) {
$("#createResult").html(data.msg);
} else {
$("#createResult").html("出现错误:" + data.msg);
}
},
error: function(jqXHR){
alert("发生错误:" + jqXHR.status);
},
});
});
});

源码

跨域

一个域名地址的组成:

http:// www . abc.com : 8080 / script/jquery.js
协议 子域名 主域名 端口号 请求资源地址

当协议、子域名、主域名、端口号中任意一个不相同时,都算作不同域。
不同域之间相互请求资源,就算做“跨域”

跨域
JavaScript 出于安全方面的考虑,不允许跨域调用其他页面的对象。
简单理解就是 JavaScript 同源策略的限制。

处理跨域方式——代理
后端做代理

处理跨域方式 —— jsonp

JSONP 可用于解决主流浏览器的跨域数据访问的问题,只能 GET 请求。借助标签可以实现跨域的特性。

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$("#search").click(function(){ 
$.ajax({
type: "GET",
url: "http://127.0.0.1:8000/ajaxdemo/serverjsonp.php?number=" + $("#keyword").val(),
dataType: "jsonp",
jsonp: "callback",
success: function(data) {
if (data.success) {
$("#searchResult").html(data.msg);
} else {
$("#searchResult").html("出现错误:" + data.msg);
}
},
error: function(jqXHR){
alert("发生错误:" + jqXHR.status);
},
});
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php
//设置页面内容是html编码格式是utf-8
//header("Content-Type: text/plain;charset=utf-8");
header("Content-Type: application/json;charset=utf-8");
//header("Content-Type: text/xml;charset=utf-8");
//header("Content-Type: text/html;charset=utf-8");
//header("Content-Type: application/javascript;charset=utf-8");

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
(
array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
);

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
create();
}

//通过员工编号搜索员工
function search(){
$jsonp = $_GET["callback"];
//检查是否有员工编号的参数
//isset检测变量是否设置;empty判断值为否为空
//超全局变量 $_GET 和 $_POST 用于收集表单数据
if (!isset($_GET["number"]) || empty($_GET["number"])) {
echo $jsonp . '({"success":false,"msg":"参数错误"})';
return;
}
//函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
//global 关键词用于访问函数内的全局变量
global $staff;
//获取number参数
$number = $_GET["number"];
$result = $jsonp . '({"success":false,"msg":"没有找到员工。"})';

//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
if ($value["number"] == $number) {
$result = $jsonp . '({"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
',员工姓名:' . $value["name"] .
',员工性别:' . $value["sex"] .
',员工职位:' . $value["job"] . '"})';
break;
}
}
echo $result;
}

//创建员工
function create(){
//判断信息是否填写完全
if (!isset($_POST["name"]) || empty($_POST["name"])
|| !isset($_POST["number"]) || empty($_POST["number"])
|| !isset($_POST["sex"]) || empty($_POST["sex"])
|| !isset($_POST["job"]) || empty($_POST["job"])) {
echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
return;
}
//TODO: 获取POST表单数据并保存到数据库

//提示保存成功
echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}

?>

源码

处理跨域方式 —— XHR2

HTML5 提供的 XMLHttpRequest Level2 已经实现了跨域访问以及其他的一些新功能
IE10 以下的版本都不支持
在服务器端做一些小小的改造即可:
header(‘Access-Control-Allow-Origin:*’)
header(‘Access-Control-Allow-Methods:POST,GET’)

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php
//设置页面内容是html编码格式是utf-8
//header("Content-Type: text/plain;charset=utf-8");
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST,GET');
header('Access-Control-Allow-Credentials:true');
header("Content-Type: application/json;charset=utf-8");
//header("Content-Type: text/xml;charset=utf-8");
//header("Content-Type: text/html;charset=utf-8");
//header("Content-Type: application/javascript;charset=utf-8");

//定义一个多维数组,包含员工的信息,每条员工信息为一个数组
$staff = array
(
array("name" => "洪七", "number" => "101", "sex" => "男", "job" => "总经理"),
array("name" => "郭靖", "number" => "102", "sex" => "男", "job" => "开发工程师"),
array("name" => "黄蓉", "number" => "103", "sex" => "女", "job" => "产品经理")
);

//判断如果是get请求,则进行搜索;如果是POST请求,则进行新建
//$_SERVER是一个超全局变量,在一个脚本的全部作用域中都可用,不用使用global关键字
//$_SERVER["REQUEST_METHOD"]返回访问页面使用的请求方法
if ($_SERVER["REQUEST_METHOD"] == "GET") {
search();
} elseif ($_SERVER["REQUEST_METHOD"] == "POST"){
create();
}

//通过员工编号搜索员工
function search(){
//检查是否有员工编号的参数
//isset检测变量是否设置;empty判断值为否为空
//超全局变量 $_GET 和 $_POST 用于收集表单数据
if (!isset($_GET["number"]) || empty($_GET["number"])) {
echo '{"success":false,"msg":"参数错误"}';
return;
}
//函数之外声明的变量拥有 Global 作用域,只能在函数以外进行访问。
//global 关键词用于访问函数内的全局变量
global $staff;
//获取number参数
$number = $_GET["number"];
$result = '{"success":false,"msg":"没有找到员工。"}';

//遍历$staff多维数组,查找key值为number的员工是否存在,如果存在,则修改返回结果
foreach ($staff as $value) {
if ($value["number"] == $number) {
$result = '{"success":true,"msg":"找到员工:员工编号:' . $value["number"] .
',员工姓名:' . $value["name"] .
',员工性别:' . $value["sex"] .
',员工职位:' . $value["job"] . '"}';
break;
}
}
echo $result;
}

//创建员工
function create(){
//判断信息是否填写完全
if (!isset($_POST["name"]) || empty($_POST["name"])
|| !isset($_POST["number"]) || empty($_POST["number"])
|| !isset($_POST["sex"]) || empty($_POST["sex"])
|| !isset($_POST["job"]) || empty($_POST["job"])) {
echo '{"success":false,"msg":"参数错误,员工信息填写不全"}';
return;
}
//TODO: 获取POST表单数据并保存到数据库

//提示保存成功
echo '{"success":true,"msg":"员工:' . $_POST["name"] . ' 信息保存成功!"}';
}

?>

产生跨域问题的原因

浏览器限制
跨域
XHR(XMLHttpRequest)请求

解决思路

解决思路

全面解决跨域问题

浏览器禁止检查

命令行参数启动

1
C:\Users\UserName\AppData\Local\Google\Chrome\Application\chrome.exe --disable-web-security --user-data-dir

JSONP

JSONP 是什么?
利用 script 标签请求资源可以跨域,来解决跨域问题。

JSONP 后台要改动。

JSONP 发出去的请求 Type 是 script ,而 Ajax 发出去的是 Type 是 xhr。
Ajax 返回的类型是 json,而 JSONP 返回的类型是 JavaScript。
JSONP 请求地址加了 callback 参数 ,还有随机数是防止被缓存

JSONP

服务器需要改动代码支持
只支持 GET 方法

最常见的 JAVAEE 架构

JAVAEE 架构

跨域解决方向

被调用方解决:
被调用方解决
服务器端实现
Nginx 配置
Apache 配置

浏览器在发送跨域请求的时候先判断是不是简单请求,简单请求浏览器是先执行后判断,非简单请求会先发一个预检命令,检查通过之后才发跨域请求。
如何判断,根据请求中(Origin)源头来判断。
服务端 filter 代码实现原理 所有的请求都经过 filter。filter 中增加响应头。

调用方解决
隐藏跨域

简单请求和非简单请求

简单请求
方法为:GET、POST、HEAD
请求 header 里面:无自定义头
Content-Type 为以下几种:
text/plain
multipart/form-data
application/x-www-form-urlencoded

工作中常见的非简单请求有
put,delete 方法的 ajax 请求
发送 json 格式的 ajax 请求
带自定义头的 ajax 请求

缓存预检命令:Access-Control-Max-Age:”3600” 毫秒数

1
2
3
4
5
6
7
8
9
10
$.ajax({
type: "get",
url: base + "/getCookie",
xhrFields: {
withCredentials: true
},
success: function(json){
result = json;
}
})

后端要加

Access-Control-Allow-Origin 不能为 * 必须是完全匹配
Access-Control-Allow-Credentials 设置为 true
可以把 请求头的 Origin 设置为 Access-Control-Allow-Origin 的值来应对多个跨域

带自定义头的跨域

1
2
3
4
5
6
7
8
9
10
11
12
13
$.ajax({
type: "get",
url: base + "/getCookie",
headers: {
"x-header1" :"AAA"
},
beforeSend: function(xhr){
xhr.setRequestHeader('x-header2','BBB')
},
success: function(json){
result = json;
}
})

后端要加
Access-Control-Allow-Headers,“x-header1”,”x-header2”
可以把 请求头中 Access-Control-Request-Headers 作为 Access-Control-Allow-Headers 的值。

nginx 解决方案

可以通过搜索 hosts* 来找到 windows host配置

windows host配置
C:\Windows\System32\drivers\etc\hosts 内添加映射
127.0.0.1 b.com

在 nginx-1.16.1 目录下 conf 文件下 nginx.conf 增加 include servers/*.conf;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server{
listen 80;
server_name b.com;

location / {
proxy_pass http://localhost:4000;
add_header Access-Control-Allow-Methods *;
add_header Access-Control-Max-Age 3600;
add_header Access-Control-Allow-Credentials true;
add_header Access-Control-Allow-Origin $http_origin;
add_header Access-Control-Allow-Headers $http_access-control-request-headers;

if ($request_method = OPTIONS){
return 200;
}
}
}

Apache 解决方案

可以搜索 httpd.conf 来找到 Apache 的配置文件

Apache 配置
Apache2.4.39\conf\httpd.conf

1
2
3
4
5
6
7
# 去掉下列的注释 #
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
#Include conf/vhosts.conf
#LoadModule proxy_module modules/mod_proxy.so
#LoadModule proxy_http_module modules/mod_proxy_http.so
#LoadModule headers_module modules/mod_headers.so
#LoadModule rewrite_module modules/mod_rewrite.so

conf/vhosts.conf 配置文件中添加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<VirtualHost *:80>
ServerName test.com
ErrorLog logs/test.com-error_log
CustomLog logs/test.com-access_log common
ProxyPass / http://localhost:4000/

# 把请求头的 origin 值返回到 Access-Control-Allow-Origin 字段
Header always set Access-Control-Allow-Origin "expr=%{req:origin}"

# 把请求头的 Access-Control-Request-Headers 值返回到 Access-Control-Allow-Headers 字段
Header always set Access-Control-Allow-Headers "expr=%{req:Access-Control-Request-Headers}"
Header always set Access-Control-Allow-Methods "*"
Header always set Access-Control-Allow-Credentials "true"
Header always set Access-Control-Max-Age "3600"

# 处理预检命令 OPTIONS,直接返回 204
RewriteEngine On
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ "/" [R=204,L]
</VirtualHost>

隐藏跨域 nginx

反向代理

1
2
3
4
5
6
7
8
9
10
11
12
server{
listen 80;
server_name a.com

location / {
proxy_pass http://localhost:8081;
}

location /ajaxserver {
proxy_pass http://localhost:8080/test/;
}
}

隐藏跨域 Apache

<VirtualHost *:80>
ServerName test.com
ErrorLog logs/test.com-error_log
CustomLog logs/test.com-access_log common

ProxyPass / http://localhost:8081/
ProxyPass /ajaxserverapache http://localhost:8080/test
---

以上是我对下列视频及文章的归纳和总结。
Ajax全接触
Ajax跨域完全讲解


相关资料
前端测试框架对比

作者

Fallen-down

发布于

2020-03-26

更新于

2020-06-18

许可协议

You need to set install_url to use ShareThis. Please set it in _config.yml.
You forgot to set the business or currency_code for Paypal. Please set it in _config.yml.

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
You need to set client_id and slot_id to show this AD unit. Please set it in _config.yml.