Fork me on GitHub

前端的网络请求方式

网络请求 跨域处理方式
https://mp.weixin.qq.com/s/zSB7X2ka6GtxtupUtal7ig

AJAX 请求

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
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP');
//请求参数
postData = {
'name1': 'value1',
'name2': 'value2'
}
xhr.open('POST', 'http://localhost:8000/login', true);

//get
postData = (function (value) {
let oStr = '';
for (var key in value) {
oStr += key + '=' + value[key] + '&';
}
return oStr
})(postData)
url=url+'?'+posData;
xhr.open('GET', url.replace(/\?$/g,'), true);
// 接收返回值
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 ) {
if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
console.log(xhr.responseText)
}
}
}

console.log(postData)
//设置请求头
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
//异常处理
xhr.onerror = function () {
console.log('Network request failed!')
}
//跨域携带cookie
xhr.withCredentials = true;
//发出请求
xhr.send(postData)

函数

open

用于初始化一个请求

1
xhr.open(method, url, async)
  • method:请求方法,get/post
  • url:请求的 url
  • async:是否为异步请求。true/false

send

用于发送 HTTP 请求

1
xhr.send(param)
  • param:请求参数可以是stringBlob 等类型

abort
如果该请求已被发出,abort() 方法将终止该请求。当一个请求被终止,它的 readyState 属性将被置为 0( UNSENT )

1
xhr.abort()

setRequestHeader
用于设置 HTTP 请求头,此方法必须在 open()方法和 send()之间调用

1
xhr.setRequestHeader(header, value)

getResponseHeader
用于获取 HTTP 返回头,如果在返回头中有多个一样的名称,那么 返回值就会用逗号和空格将 值分隔的字符串。

1
var header = xhr.getResponseHeader(name)

属性

readyState
用来标识当前 XMLHttpRequest 对象所处的状态,XMLHttpRequest 对象总是位于下列状态中的一个:

状态 描述
0 UNSENT 代理被创建,但尚未调用 open()方法
1 OPENED open 方法已经被调用
2 HEADERS_RECEIVED send()方法已经被调用,并且头部和状态已经可获得
3 LOADING 下载中,responseText 属性已经包含部分数
4 DONE 下载操作已完成

status
表示 http 请求的状态,初始值为 0。如果服务器没有现实指定状态码,那么 status 将被设置为默认值,即 200。

responseType
表示响应的数据类型,并允许我们手动设置,如果结果为空,默认为 text 类型
|值|描述|
|-|-|
|””|将 responseType 设置空字符串与设置为 text 相同,是默认类型|
|”arraybuffer”|response 是一个包含二进制数据的 JavaScriptArrayBuffer|
|”blob”|response 是一个包含二进制数据的 Blob 对象|
|”document”|responses 是一个 HTML Document 或者 XML XMLDocument,取决于接收到数据的 MIME 类型|
|”json”|response 是一个 JavaScript 对象,这个对象时通过将接收到的数据类型为 JSON 解析|
|”text”|response 是包含在 DOMString 对象中的文本|

response
返回响应的正文,返回的类型由上面的 responseType 决定

withCredentials
ajax 请求默认会携带同源请求的 cookie,而跨域请求则不会携带 cookie,设置 xhr 的 withCredentials 的属性为 true 将允许携带跨域 cookie

事件回调

onreadystatechange

1
xhr.onreadystatechange = callback

当 readyState 属性发生变化时,callback 会被触发
onloadstart

1
2

xhr.onloadstart = callback

在 ajax 请求发送之前(readyState==1 后,readyState==2 前),callback 会被触发
onprogress

1
2
3
xhr.onprogress = function(event){
console.log(event.loaded/event.total)
}

回调函数可以获取资源总大小 total,已经加装的资源大小 loaded,用着两个值可以计算加载进度。
onload

1
xhr.onload = callback

当一个资源及其依赖已完成加载时,将触发 callback,通常我们会在 onload 事件中处理返回值。 ##异常处理

onerror

1
xhr.onerror = callback

ajax 资源加载失败触发 callback

ontimeout

1
xhr.ontimeout = callback

当进度由于预定时间到期而终止时,会触发 callback,超时时间可使用 timeout 属性进行设置。

JQuery 对 Ajax 的封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$.ajax({
url: 'http://localhost:8000/login',//请求接口
type: 'POST',//请求类型
dataType: 'json', //设置返回值类型
contentType: 'application/json', //设置请求参数类型
headers: {
'Content-Type': 'application/json'
}, //设置请求头
xhrFields: {
widthCredentials: true
}, //跨域携带cookie
data: JSON.stringify({
a: [{
b: 1,
a: 1
}]
}),
error: function (xhr, status) { //错误处理
console.log(xhr, status);
},
success: function (data, status) { //获取结果
console.log(data, status)
}
})
  • url:请地址和接口
  • type:请求类型。String。”POST”/“GET”,默认为”GET”
  • timeout:Number 设置请求超时时间(毫秒),此设置将覆盖全局设置。
  • success:Function,请求成功的回调函数
  • error:Function,请求失败的回调函数
  • jsonp:String,jsonp:’callback’
  • jsonpCallback: String,函数名”onJsonPLoad”,
  • dataType:

    • ‘xml’:返回 XML 文档,可用 JQuery 处理
    • ‘html’:返回纯文本 HTML 信息,包含的 script 标签会在插入 dom 时执行
    • ‘script’:返回纯文本 JavaScript 代码。不会自动缓存结果。除非设置了’cache’参数。注意:在远程请求时(不同域下),所有 POST 请求都将转为 GET 请求,(因为将使用 DOM 的 script 标签来加载)
    • ‘json’:返回 JSON 数据
    • ‘jsonp’:JSONP 格式,使用 JSONP 形式调用函数时如”myurl?callback=?”,jQuery 将自动替换?为正确的函数名,以执行回调函数。
    • ‘text’:返回纯文字字符串
  • data:类型 String 使用 JSON.stringify()转码

  • complete:Function,请求完成以后回调函数,请求成功或失败之后均会调用
  • async:Boolean,默认为 true,异步请求,如需发送同步请求,设为 false
  • contentType:String,默认’application/x-www-form-urlencoded’。发送信息至服务器时内容编码的类型。
    一般键值对的形式,默认的编码方法可以处理,如 data:{a:1, b:2, c:3}
    但是 json 对象存在对象嵌套数组,数组包含对象,上面的编码方式就无法处理,需要将参数利用 JSON.stringify()处理,data:JSON.stringify({a:1, [{b:2, c:3}]})

Fetch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  const options = {
method: 'POST', //请求方法
headers: {
'content-Type': 'application/json',
}, //设置请求头
body: JSON.stringify({
name: '123'
}),//请求参数

credentials: 'include', //cookie设置
mode: 'cors', //跨域
}
fetch('http://localhost:8000/login1', options)
.then(res => {
return res.json()
})
.then(myJson => {
console.log(myJson)
})
.catch(err => {
console.log(err)
})
  • mode:是否跨域

    • cors:允许发送跨域请求,服务器需要设置 cors 响应头
    1
    2
    3
    res.header("Access-Control-Allow-Origin", "http://localhost:8888");
    //或
    res.header("Access-Control-Allow-Origin", "*");
    • no-cors:常用于在跨域不带 CORS 场景下, 此时请求发出并且会有响应,但是此时 type 为“opaque”, status 为 0 ,js 获取不到返回数据。
    • same-origin:在同域下请求
    • cors-with-forced-preflight:在请求前进行 preflight 检查
  • credentials:请求是否需要携带 cookie

    • omit:不携带认证凭证 Cookie
    • same-origin:在同源站点下包含凭证
    • include:对所有网址包含认证凭证
  1. 跨域请求,不携带 cookie
  • 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const options = {
method: 'POST', //请求参数
headers: {
'content-Type': 'application/json;charset=utf-8',
}, //设置请求头
body: JSON.stringify({
name: '123'
}),
credentials: 'omit', //或same-origin
mode: 'cors', //跨域
}

fetch('http://localhost:8000/login1', options)
.then(res => {
return res.json()
})
.then(myJson => {
console.log(myJson)
})
.catch(err => {
console.log(err)
})
  • node 服务器端配置
    响应头设置
1
2
3
4
5
6
7
8
9
10
//处理请求跨域
server.all("*", function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://localhost:8888");//可以是*,表示通配符匹配全部地址,或者是,连接的多个地址
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);//响应头
res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
next();
});
  1. 客户端请求允许携带 cookie
  • 客户端
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 const options = {
method: 'POST', //请求参数
headers: {
'content-Type': 'application/json;charset=utf-8',
}, //设置请求头
body: JSON.stringify({
name: '123'
}),
credentials: 'include', //允许跨域cookie
mode: 'cors', //跨域
}

fetch('http://localhost:8000/login1', options)
.then(res => {
return res.json()
})
.then(myJson => {
console.log(myJson)
})
.catch(err => {
console.log(err)
})
  • 服务器
1
2
3
4
5
6
7
8
9
10
11
//处理请求跨域
server.all("*", function(req, res, next) {
res.setHeader("Access-Control-Allow-Origin", "http://localhost:8888");//必须是地址,不能写通配符
res.setHeader(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization"
);
res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.setHeader("Access-Control-Allow-Credentials", true);//表示是否允许发送Cookie
next();
});

获取请求的 cookie

1
req.headers.cookie

Fetch 执行完毕后,不能直接在 response 中获取到返回值必须调用 text()、json()、blob()、formData()函数才能获取返回值。每次调用text()、json()等函数后会将 bodyUsed 变量变为 true,用来标识返回值已经读取过了,下一次再读取直接抛出 TypeError(‘Already read’)。
fetch 是一个非常底层的 API,它的问题:

  • 不能直接传递 JavaScript 对象作为参数
  • 需要自己判断返回值类型,并执行响应获取返回值的方法
  • 获取返回值方法只能调用一次,不能调用多次
  • 无法正常捕获异常
  • 老版浏览器默认不会携带 cookie
  • 不支持 jsonp

对 fetch 的封装

请求参数的处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 function stringify(url, data) {
var dataString = url.indexOf('?') === -1 ? '?' : '&';
for (var key in data) {
dataString += key + '=' + data[key] + '&';
}
return dataString;
}
if (request.formData) {
request.body = request.data;
} else if (/^get$/i.test(request.method)) {
request.url = `${request.url}${stringify(request.url, request.data)}`;
} else if (request.form) { //表单提交
request.headers = {
'Content-Type': 'application/x-www-urlencoded;charset=UTF-8'
}
request.body = stringify(request.data)
} else {
request.headers = {
'Content-Type': 'application/json;charset=UTF-8'
}
request.body = JSON.stringify(request.data)
}

异常处理

1
2
3
4
5
6
7
8
9
10
11
.then(response => {
console.log(response)
if (response.ok) {
return Promise.resolve(response);
} else {
const error = new Error(`请求失败!状态码:${response.status},失败信息:${response
.statusText}`)
error.response = response;
return Promise.reject(error)
}
})

返回值处理

1
2
3
4
5
6
7
8
 .then(response => {
let contentType = response.headers.get('content-type');
if (contentType.includes('application/json')) {
return response.json()
} else {
return response.text()
}
})
-------------本文结束感谢阅读-------------