0%

网络请求

基础知识

浏览器天生具发送HTTP请求的能力,比如在在址栏输入内容,提交FORM表单等。本章来学习通过JS程序来管理HTTP请求的能力。

局部更新

最传统的网络请求是通过提交表单进行的,表单的提交、地址栏输入内容,都会导致界面的全部刷新。

而使用JS脚本发送HTTP请求,**不会带来页面的刷新**,我们可以向后台请求数据,拿到数据后渲染到界面上,会有页面局部更新的效果,所以用户体验非常好。

请求方式

请求方式有很多,一般使用以下4种

  1. GET

    该方式一般应用于 单纯获取数据 的业务,请求参数包含在URL内

  2. POST

    该方式一般应用于 向服务器提交数据 ,请求参数置于请求体当中

  3. PUT

    该方式一般应用于 更改服务器数据 的业务

  4. DELETE

    该方式一般应用于 删除服务器数据 的业务


基本使用

方法介绍

open

open()用于创建请求(单纯创建,并不发送)

注意:如果open()methodGET,则url需要自带参数。

  • get请求就是通过url进行的
  • 参数格式key=val多个参数之间使用&隔开
参数 说明
method 请求方式
url 请求地址
async 是否异步(默认为true)
send

send(body)用于发送请求

  • 若请求方式为GET,则不用为其传递参数(get请求在url中传参)。
  • 若请求方式为POST,则要把参数传递到send(body)
参数 说明
body 请求体(要发送的数据)(字符串类型)
1
2
3
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:5500/test?name=Ashun");
xhr.send();
1
2
3
4
5
let xhr = new XMLHttpRequest();
xhr.setRequestHeader("Content-Type", "application/json");
xhr.open("POST", "http://127.0.0.1:5500/test");
let body={ name: "Ashun" };
xhr.send(body);
setRequestHeader

用于设置请求头,一般我们会指定编码方式。

  • 请求方式为POST,需要设置请求头的编码方式,GET不用设置。
参数 说明
header 请求头的key(字符串类型)
vlaue 请求头的value(字符串类型)
  • 当传递的参数为form表单形式的数据,则需要设置Content-type:application/x-www-form-urlencoded

    1
    2
    3
    4
    let xhr = new XMLHttpRequest();
    xhr.open("post", "http://127.0.0.1:5500/test");
    xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    xhr.send("name=Ashun");
  • 若传递的参数为json字符串,则需要设置Content-type:application/json

    1
    2
    3
    4
    let xhr = new XMLHttpRequest();
    xhr.open("post", "http://127.0.0.1:5500/test");
    xhr.setRequestHeader("Content-Type", "application/json");
    xhr.send(JSON.stringify({ name: "Ashun" }));
getAllResponseHeaders

用于获取响应头,返回所有的响应头数据(字符串类型)。

getResponseHeader

获取响应头中指定header的值,返回该响应头的数据(字符串类型)。

参数 描述
header 响应头的key(字符串类型)
abort

终止请求。


属性介绍

status

HTTP状态码,如200/404等等。

statusText

HTTP状态文本(字符串),与状态码对应如:OK、NotFound…

status statusText
200 OK
404 NotFound
readyState

xhr状态码,使用XmlHttpRequest时共有5种状态。

状态值 描述
0 未初始化,尚未调用open()方法
1 初始化,调用了open()方法,未调用send()方法
2 发送,已经调用了send()方法,未接收到响应
3 接收,已经接收到部分响应数据
4 完成,已经接收到全部响应数据
responseText

这是服务器返回的数据(字符串类型)


回调函数

以下回调函数需要我们手动配置,当达到对应的状态后自动回调

onload

当请求完毕后,会自动执行该函数。

1
2
3
4
xhr.onload = () => {
console.log(xhr.getAllResponseHeaders());
console.log(xhr.responseText);
};
onerror

当请求产生错误时,会自动执行该函数

1
2
3
4
xhr.onerror = () => {
console.log(xhr.status);
console.log("请求失败");
};
onreadystatechange

当xhr状态码改变时,自动回调。 可以结合xhr、HTTP状态码,对请求是否成功做出判断。

1
2
3
4
5
6
7
8
9
10
xhr.onreadystatechange = () => {
//请求结束,并且成功
if (xhr.readyState == 4 && xhr.status === 200) {
console.log(xhr.responseText);
} else if (xhr.status == 404) {
console.log("请求失败");
} else if(xhr.status == 500){
console.log("服务器内部错误")
}
};

FormData

FormData是JavaScript的一个内置对象,当满足以下使用条件时,会使请求变得更加简便

  • 使用表单form发送请求
  • 请求方式为POST

使用步骤

  1. 实例化对象,并传入表单Domlet formdata=new FormData(formDom)
  2. 将其置于请求体中xhr.send(formdata)

FormData对象,会自动将传入的表单Dom中的数据,转化为正确的形式(key=val多个参数使用&隔开)。

通过使用步骤可知,使用FormData请求的方式必须为POST(请求参数置于请求体当中)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<form>
<input type="text" name="username" />
<input type="password" name="password" />
<button type="button">Login</button>
</form>
<script>
let formDom = document.querySelector("form");
let xhr = new XMLHttpRequest();

let btn = document.querySelector("button");
btn.addEventListener("click", () => {
//如果请求的是同源服务器,那么可以省略掉协议、域名、端口
xhr.open("post", "/formdata0");
let formdata = new FormData(formDom);
xhr.send(formdata);
xhr.onload = () => {
xhr.status == 200 && console.log(xhr.responseText);
};
xhr.onerror = () => {
console.log(xhr.status);
};
});
</script>

若不使用FormData,则我们需要做更多的操作

  • 收集表单元素的数据,并以正确格式拼接(FormData自动收集)
  • 发送请求前,需要设置请求头(FormData不需要)

请求/返回类型

页面向服务器提交数据、服务器返回给浏览器数据。在这两个过程中,若传递的是引用类型数据,则最终都需要转化为JSON String

页面向服务器提交数据

向服务器提交数据,请求方式为POST,若发送的数据为引用类型,需要设置响应头信息Content-Type:application/json,且放入请求体中的数据必须是JSON字符串(send(JSON.stringify(data)))

服务器返回给浏览器数据

服务器可以直接返回页面 引用类型 数据,但是页面接收到的还是JSON String,需要将数据反序列化得到真正的数据。

发送请求

  • 若直接接收服务器返回的引用类型数据,则为JSON String
1
2
3
4
5
6
7
8
let xhr = new XMLHttpRequest();
xhr.open("post", "/test");
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name: "Ashun" }));
xhr.onload = function () {
console.log(typeof xhr.responseText, xhr.responseText);
};
//结果: string { "name": "Ashun" }
  • 可以判断响应头信息,返回对应的数据
1
2
3
4
5
6
7
8
9
……
xhr.onload = function () {
let isJson = xhr
.getResponseHeader("Content-Type")
.includes("application/json");
isJson
? console.log(JSON.parse(xhr.responseText))
: console.log(xhr.responseText);
};

服务器响应

1
2
3
4
5
6
7
8
9
app.post("/test", (req, res) => {
let postRes = "";
req.on("data", (chunk) => {
postRes += chunk;
});
req.on("end", () => {
res.send(JSON.parse(postRes));
});
});

简单封装

​ 上面我们了解了Ajax的基本使用,现在我们可以封装一个Ajax请求处理函数,让请求更加的简便。

需求

每次请求的过程,都需要经历以下步骤

  • 创建xhr对象
  • 初始化请求
  • 设置请求头(post)
  • 发送请求
  • 请求完成后的处理

我们可以将上述步骤抽离出来,把需要改变的部分暴露出去,让外部来决定,即:请求方式、请求地址、是否设置请求头、传递的参数、请求完毕后的具体操作。

还要根据服务器返回不同类型的结果进行判断,正确提交给外部。

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
function ajax(preset) {
let options = Object.assign(
{
type: "get",
url: "",
data: {},
header: {
["Content-Type"]: "application/x-www-form-urlencoded",
},
success: (result, xhr) => console.log(result),
error: (xhr) => console.log(xhr.status),
},
preset
);
//数据拼接
let queryStr = Object.entries(options.data)
.map(([key, val]) => `${key}=${val}`)
.join("&");

//请求处理
const xhr = new XMLHttpRequest();
if (options.type == "get") {
xhr.open(options.type, `${options.url}?${queryStr}`);
xhr.send();
}
if (options.type == "post") {
xhr.open(options.type, options.url);
//设置header
Object.entries(options.header).forEach(([prop, val]) => {
xhr.setRequestHeader(prop, val);
});
let contentType = options.header["Content-Type"];
//根据请求头信息,判断发送数据的形式
contentType.includes("json")
? xhr.send(JSON.stringify(options.data))
: xhr.send(queryStr);
}
xhr.onload = () => {
//根据响应头信息,返回对应类型的数据
let isJson = xhr.getResponseHeader("Content-Type").includes("json");
let result = isJson ? JSON.parse(xhr.responseText) : xhr.responseText;
//根据HTTP状态码,执行不同的callback
xhr.status == 200 ? options.success(result, xhr) : options.error(xhr);
};
}

实例测试

1
2
3
4
5
6
7
8
9
10
11
12
13
ajax({
type: "post",
url: "test",
header: {
["Content-Type"]: "application/json",
},
data: {
name: "Ashun",
},
success: (res, xhr) => {
console.log(res, xhr.status);
},
});

FETCH

fetch(url,{options})是JS升级后提供的更简便的网络请求的操作方法,内部使用Promise来完成异步请求。

  • response.json()处理为JSON对象,将后台返回的JSON字符串处理为JSON对象
  • response.text()处理为TEXT类型数据
  • response.blog()处理为Blog二进制数据

请示步骤

使用fetch方法发送异步请求需要分以下两步操作

响应头解析

第一步对服务器返回的响应头进行解析,会接到Response类创建的对象实例,里面包含以下属性。

  • status:HTTP状态码
  • ok:状态码为200-299 时为true的布尔值
响应内容解析

第二步对返回的 保存在response.body 中的响应结果进行解析,支持了以下几种方式对结果进行解析,且以下方法都默认返回Promise

  • response.json()处理为JSON对象,将后台返回的JSON字符串处理为JSON对象
  • response.text()处理为TEXT类型数据
  • response.blog()处理为Blog二进制数据

以上方法不能同时使用,因为使用一个方法后数据已经被处理,其他方法就不可以操作了

实例操作

下面来体验使用fetch发送请求

后台服务

1
2
3
app.get("/test", (req, res) => {
res.send({ name: "阿顺" });
});

发送请求

以下为发送get请求

1
2
3
4
5
fetch("/test").then((result) => {
return result.json();
})
.then((result) => console.log(result))
.catch((err) => console.log(err));

POST

发送POST请求需要设置请求头Request header

发送请求

  • 发送的JSON类型需要设置请求头为 application/json;charset=utf-8
1
2
3
4
5
6
7
8
9
10
11
12
13
async function post() {
let response = await fetch("/test", {
method: "post",
headers: {
"Content-Type": "application/json;charset=utf-8",
},
body: JSON.stringify({ name: "阿顺", title: "Ashuntefannao" }),
});
if (response.ok) {
let result = await response.json();
console.log(result);
}
}

后台响应

将数据原样返回

1
2
3
4
5
6
7
8
9
app.post("/test", (req, res) => {
let postRes = "";
req.on("data", (chunk) => {
postRes += chunk;
});
req.on("end", () => {
res.send(JSON.parse(postRes));
});
});