JavaScript 中的AJAX调用

Posted by cl9000 on October 14, 2020

人生的价值,并不是用时间,而是用深度去衡量的。 ——<列夫·托尔斯泰>

作者:Ashish Lahoti
译者:cl9000
来源: https://codingnconcepts.com/javascript/how-to-make-ajax-calls-in-javascript/

在本教程中,我们将学习如何使用 JavaScript 进行 AJAX调用。

1. AJAX

术语 AJAX 代表异步JavaScriptXML

术语AJAXJavaScript中用于发出异步网络请求以获取资源。资源并不局限于XML,正如术语所表明的那样,这令人困惑。术语AJAX也用于获取JSON、HTML或纯文本等资源。你可能已经听说过这个术语。

有多种方式可以发出网络请求并从服务器获取数据。

2. XMLHttpRequest

以前,XMLHttpRequest对象(也简称为XHR)用于从服务器异步检索数据。

名称中出现XML是因为它首先用于检索XML数据。现在,它还可以用于检索JSON、HTML或纯文本

2.1. Example: GET

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function success() {
var data = JSON.parse(this.responseText);
console.log(data);
}

function error(err) {
console.log('Error Occurred :', err);
}

var xhr = new XMLHttpRequest();
xhr.onload = success;
xhr.onerror = error;
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1");
xhr.send();

我们看到,要发出一个简单的GET请求,我们需要两个侦听器来处理请求的成功和失败。我们还需要调用 open()send() 方法。来自服务器的响应存储在 responseText 变量中,该变量使用JSON.parse() 转换为JavaScript 对象。

2.2. Example: POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function success() {
var data = JSON.parse(this.responseText);
console.log(data);
}

function error(err) {
console.log('Error Occurred :', err);
}

var xhr = new XMLHttpRequest();
xhr.onload = success;
xhr.onerror = error;
xhr.open("POST", "https://jsonplaceholder.typicode.com/posts");
xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8");
xhr.send(JSON.stringify({
title: 'foo',
body: 'bar',
userId: 1
})
);

我们看到POST请求类似于GET请求。我们需要使用setRequestHeader附加设置请求头“Content-Type”,在send方法中并使用JSON.Stringify()JSON体作为字符串发送进行。

2.3. XMLHttpRequest vs Fetch

多年来,我们一直使用 XMLHttpRequest 来请求数据。现代的fetch() API允许您进行类似于 XMLHttpRequest(XHR)的网络请求。主要的区别是fetch() API使用了Promises,它支持更简单、更干净的API,避免了回调地狱和必须记住XMLHttpRequest复杂的API。

3. Fetch API

Fetch是一个用于进行AJAX调用的原生JavaScript API,它得到了大多数浏览器的支持,现在被广泛使用。

3.1. API 基本用法

1
2
3
4
5
6
7
fetch(url, options)
.then(response => {
// handle response data
})
.catch(err => {
// handle errors
});
API参数

fetch() API有两个参数:

  • Url 是一个必传参数,它是您想要获取的资源的路径。
  • Options 是一个可选参数。您不需要提供此参数来进行简单的GET请求。您需要传递此参数来提供关于请求的附加信息,例如
    • method: GET | POST | PUT | DELETE | PATCH
    • headers: 请求头对象,例如{" Content-type ": " application/json;charset = utf - 8”}
    • mode: cors | no-cors |同源|导航
    • Cache: default | reload | no-cache
    • body: 请求正文,通常用于POST请求
API返回Promise对象

fetch() API返回一个 promise 对象。

如果存在网络错误,承诺将被拒绝,这将在 .catch() 块中处理。
如果有来自服务器的带有任何状态码(如200、404、500)的响应,则承诺将被解析。响应对象可以在 .then() 块中处理。

错误处理

请注意,我们期望成功响应的状态码是200 (OK状态),但是fetch() API解决了这个承诺,即使响应带有错误状态码,比如404(资源未找到)和500(内部服务器错误)。我们需要在.then()块中显式地处理这些。

我们可以在响应对象中看到HTTP-status:

status—HTTP状态代码,例如200。
ok - boolean,如果HTTP状态码是200-299则为true。

3.2. Example: GET

1
2
3
4
5
const getTodoItem = fetch('https://jsonplaceholder.typicode.com/todos/1')
.then(response => response.json())
.catch(err => console.error(err));

getTodoItem.then(response => console.log(response));

Response
➤ { userId: 1, id: 1, title: “delectus aut autem”, completed: false }

上述代码中需要注意的两件事:-

  • fetch API返回一个promise对象,我们可以把它赋值给一个变量,然后再执行。
  • 你必须另外调用response.JSON()来将响应对象转换为JSON
错误处理

让我们看看当HTTP GET请求抛出500错误时会发生什么:-

1
2
3
4
5
6
7
fetch('http://httpstat.us/500') // this API throw 500 error
.then(response => () => {
console.log("Inside first then block");
return response.json();
})
.then(json => console.log("Inside second then block", json))
.catch(err => console.error("Inside catch block:", err));

Inside first then block
ⓧ Inside catch block: SyntaxError: Unexpected token I in JSON at position 4

我们看到,即使API抛出500错误,它仍然进入第一个 then()块,在那里它不能解析错误JSON和抛出错误被catch()块捕获。

这意味着如果我们使用fetch() API:-,我们需要像这样显式地处理这样的错误

1
2
3
4
5
6
7
8
9
10
11
12
fetch('http://httpstat.us/500')
.then(handleErrors)
.then(response => response.json())
.then(response => console.log(response))
.catch(err => console.error("Inside catch block:", err));

function handleErrors(response) {
if (!response.ok) { // throw error based on custom conditions on response
throw Error(response.statusText);
}
return response;
}

Inside catch block: Error: Internal Server Error at handleErrors (Script snippet %239:9)

3.3 Example: POST

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
body: JSON.stringify({
completed: true,
title: 'new todo item',
userId: 1
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then(response => response.json())
.then(json => console.log(json))
.catch(err => console.log(err))

Response
➤ {completed: true, title: “new todo item”, userId: 1, id: 201}

上述代码中需要注意的两件事:-

  • POST请求类似于GET请求。我们需要另外在fetch() API的第二个参数中发送method、body和headers属性。
  • 我们必须明确 JSON.stringify() 要求body参数

4. Axios API

Axios API非常类似于只有很少增强的获取API。我个人更喜欢使用Axios API而不是fetch() API,原因如下

  • 为GET axios.get(), POST axios.post(),…提供不同的方法,这使您的代码更简洁
  • 考虑299++响应代码(如404,500)作为错误,可以在catch()块中处理,因此您不需要显式地处理这些错误
  • 它提供了与旧浏览器(如IE11)的向后兼容性
  • 它将响应作为JSON对象返回,因此不需要进行任何解析
  • 它将POST请求体作为JSON对象,因此不需要进行任何stringify

4.1. Example: GET

1
2
3
4
5
6
7
8
9
10
// way to include script in chrome console
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'https://unpkg.com/axios/dist/axios.min.js';
document.head.appendChild(script);


axios.get('https://jsonplaceholder.typicode.com/todos/1')
.then(response => console.log(response.data))
.catch(err => console.error(err));

Response
{ userId: 1, id: 1, title: “delectus aut autem”, completed: false }

我们看到我们直接使用 response.data 得到响应数据。与fetch() API不同,数据没有任何解析对象。

错误处理 Error Handling
1
2
3
axios.get('http://httpstat.us/500')
.then(response => console.log(response.data))
.catch(err => console.error("Inside catch block:", err));

➤ Inside catch block: Error: Network Error

4.3. Example: POST

1
2
3
4
5
6
7
axios.post('https://jsonplaceholder.typicode.com/todos', {
completed: true,
title: 'new todo item',
userId: 1
})
.then(response => console.log(response.data))
.catch(err => console.log(err))

➤ {completed: true, title: “new todo item”, userId: 1, id: 201}

我们看到POST方法非常简短和简洁。你可以直接传递请求体参数,而不是像fetch() API那样进行字符串化。

参考

关注【公众号】,了解更多。



支付宝打赏 微信打赏

赞赏一下 坚持原创技术分享,您的支持将鼓励我继续创作!