使用Typescript重构axios(六)——实现基础功能:获取响应数据
<h1 id="0-系列文章">0. 系列文章</h1><p>1.使用Typescript重构axios(一)——写在最前面<br>
2.使用Typescript重构axios(二)——项目起手,跑通流程<br>
3.使用Typescript重构axios(三)——实现基础功能:处理get请求url参数<br>
4.使用Typescript重构axios(四)——实现基础功能:处理post请求参数<br>
5.使用Typescript重构axios(五)——实现基础功能:处理请求的header<br>
6.使用Typescript重构axios(六)——实现基础功能:获取响应数据<br>
7.使用Typescript重构axios(七)——实现基础功能:处理响应header<br>
8.使用Typescript重构axios(八)——实现基础功能:处理响应data<br>
9.使用Typescript重构axios(九)——异常处理:基础版<br>
10.使用Typescript重构axios(十)——异常处理:增强版<br>
11.使用Typescript重构axios(十一)——接口扩展<br>
12.使用Typescript重构axios(十二)——增加参数<br>
13.使用Typescript重构axios(十三)——让响应数据支持泛型<br>
14.使用Typescript重构axios(十四)——实现拦截器<br>
15.使用Typescript重构axios(十五)——默认配置<br>
16.使用Typescript重构axios(十六)——请求和响应数据配置化<br>
17.使用Typescript重构axios(十七)——增加axios.create<br>
18.使用Typescript重构axios(十八)——请求取消功能:总体思路<br>
19.使用Typescript重构axios(十九)——请求取消功能:实现第二种使用方式<br>
20.使用Typescript重构axios(二十)——请求取消功能:实现第一种使用方式<br>
21.使用Typescript重构axios(二十一)——请求取消功能:添加axios.isCancel接口<br>
22.使用Typescript重构axios(二十二)——请求取消功能:收尾<br>
23.使用Typescript重构axios(二十三)——添加withCredentials属性<br>
24.使用Typescript重构axios(二十四)——防御XSRF攻击<br>
25.使用Typescript重构axios(二十五)——文件上传下载进度监控<br>
26.使用Typescript重构axios(二十六)——添加HTTP授权auth属性<br>
27.使用Typescript重构axios(二十七)——添加请求状态码合法性校验<br>
28.使用Typescript重构axios(二十八)——自定义序列化请求参数<br>
29.使用Typescript重构axios(二十九)——添加baseURL<br>
30.使用Typescript重构axios(三十)——添加axios.getUri方法<br>
31.使用Typescript重构axios(三十一)——添加axios.all和axios.spread方法<br>
32.使用Typescript重构axios(三十二)——写在最后面(总结)</p>
<p>项目源码请猛戳这里!!!</p>
<h1 id="1-前言">1. 前言</h1>
<p>在之前的文章中,不管是<code>get</code>请求还是<code>post</code>请求都已经可以正常发出了,并且在浏览器的<code>F12</code>中也都能正确的看到返回的数据了,但这仅仅是在浏览器中利用开发者工具看到返回的数据,而在代码中我们还是无法拿到返回的数据,我们期望的使用方式应该是如下样子的:</p>
<pre><code class="language-typescript">axios({
method: 'post',
url: '/api/getResponse',
data: {
a: 1,
b: 2
}
}).then((res) => {
console.log(res)
})
</code></pre>
<p>我们期望能够拿到服务端响应的数据,并且支持 <code>Promise</code> 链式调用的方式。</p>
<h1 id="2-响应数据接口定义">2. 响应数据接口定义</h1>
<p>我们先来分析下,我们都想要拿到服务端给我们响应哪些数据:首先最主要的服务端返回的数据<code>data</code>不能少,其次,例如HTTP 状态码<code>status</code>,状态消息 <code>statusText</code>,响应头 <code>headers</code>、请求配置对象 <code>config</code> 以及请求的 <code>XMLHttpRequest</code> 对象实例 <code>request</code>,这些信息我们也都想拿到,基于此,我们先在<code>src/types/index.ts</code>中定义一下服务端响应的数据接口类型<code>AxiosResponse</code>,如下:</p>
<pre><code class="language-typescript">export interface AxiosResponse {
data: any; // 服务端返回的数据
status: number; // HTTP 状态码
statusText: string; // 状态消息
headers: any; // 响应头
config: AxiosRequestConfig; // 请求配置对象
request: any; // 请求的 XMLHttpRequest 对象实例
}
</code></pre>
<p>另外,我们还期望<code>axios</code> 函数能够返回一个 <code>Promise</code> 对象,以满足我们想要的链式调用,那么我们可以定义一个 <code>AxiosPromise</code> 接口,它继承于 <code>Promise<AxiosResponse></code> 这个泛型接口:</p>
<pre><code class="language-typescript">export interface AxiosPromise extends Promise<AxiosResponse> {
}
</code></pre>
<p>这样的话,当 <code>axios</code> 返回的是 <code>AxiosPromise</code> 类型,那么 <code>resolve</code> 函数中的参数就是一个 <code>AxiosResponse</code> 类型。</p>
<p>对于一个 <code>AJAX</code> 请求的响应,我们在发送请求的时候还可以通过设置 <code>XMLHttpRequest</code>对象的<code>responseType</code>属性来指定它响应数据的类型<code>responseType</code> (responseType的MDN介绍),于是,我们可以给之前定义好的 <code>AxiosRequestConfig</code> 类型添加一个可选属性<code>responseType</code>,添加后如下:</p>
<pre><code class="language-typescript">export interface AxiosRequestConfig {
url: string;
method?: Method;
headers?: any;
data?: any;
params?: any;
responseType?: XMLHttpRequestResponseType;
}
</code></pre>
<p><code>responseType</code> 的类型是一个 <code>XMLHttpRequestResponseType</code> 类型,它的定义是 <code>"" | "arraybuffer" | "blob" | "document" | "json" | "text"</code> 字符串字面量类型。</p>
<h1 id="3-获取响应">3. 获取响应</h1>
<p>定义好响应数据的接口类型后,我们就可以来写获取响应的逻辑了。我们知道,一个完整的<code>AJAX</code>流程大致分为4步:</p>
<ol>
<li>
<p>创建XMLHttpRequest异步对象;</p>
</li>
<li>
<p>配置请求参数;</p>
</li>
<li>
<p>发送请求;</p>
</li>
<li>
<p>注册事件,获取响应数据</p>
</li>
</ol>
<p>之前在<code>src/xhr.ts</code>中我们已经完成了前3步,那么接下来我们就实现第4步:</p>
<pre><code class="language-typescript">import { AxiosPromise, AxiosRequestConfig, AxiosResponse } from "./types";
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = "get", headers, responseType } = config;
// 1.创建XMLHttpRequest异步对象
const request = new XMLHttpRequest();
// 2.配置请求参数
request.open(method.toUpperCase(), url, true);
Object.keys(headers).forEach(name => {
if (data === null && name.toLowerCase() === "content-type") {
delete headers;
}
request.setRequestHeader(name, headers);
});
if (responseType) {
request.responseType = responseType;
}
// 3.发送请求
request.send(data);
// 4.注册事件,拿到响应信息
request.onreadystatechange = function handleLoad() {
if (request.readyState !== 4) {
return;
}
const responseHeaders = request.getAllResponseHeaders();
const responseData =
responseType && responseType !== "text"
? request.response
: request.responseText;
const response: AxiosResponse = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config,
request
};
resolve(response);
};
});
}
</code></pre>
<p>这里,我们还判断了如果 <code>config</code> 中配置了 <code>responseType</code>,我们要把它设置到 <code>request.responseType</code> 中。</p>
<p>另外,当 <code>responseType</code>没有设置或者设置为<code>text</code>时,响应数据存在于<code>request.responseText</code>,其余情况,响应数据存在于<code>request.response</code>,所以我们添加了这行代码:</p>
<pre><code class="language-typescript">const responseData = responseType && responseType !== "text"? request.response: request.responseText;
</code></pre>
<p>最后,在 <code>onreadystatechange</code> 事件函数中,我们构造了 <code>AxiosResponse</code> 类型的 <code>reponse</code> 对象,并把它 <code>resolve</code> 出去。</p>
<p>修改了 <code>xhr</code> 函数,我们同样也要对应修改 <code>axios</code> 函数:</p>
<pre><code class="language-typescript">// src/index.ts
function axios(config: AxiosRequestConfig): AxiosPromise {
processConfig(config)
return xhr(config)
}
</code></pre>
<p>OK,获取响应就已经完成了,接下来,我们就可以编写<code>demo</code>来测试下效果怎么样。</p>
<h1 id="4-编写demo">4. 编写demo</h1>
<p>在 <code>examples</code> 目录下创建 <code>getResponse</code>目录,在 <code>getResponse</code>目录下创建 <code>index.html</code>:</p>
<pre><code class="language-html"><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>getResponse demo</title>
</head>
<body>
<script src="/__build__/getResponse.js"></script>
</body>
</html>
</code></pre>
<p>接着再创建 <code>app.ts</code> 作为入口文件:</p>
<pre><code class="language-javascript">import axios from "../../src/index";
axios({
method: "post",
url: "/api/getResponse",
data: {
a: 1,
b: 2
}
}).then(res => {
console.log(res);
});
axios({
method: "post",
url: "/api/getResponse",
responseType: "json",
data: {
a: 3,
b: 4
}
}).then(res => {
console.log(res);
});
</code></pre>
<p>接着在 <code>server/server.js</code> 添加新的接口路由:</p>
<pre><code class="language-javascript">router.post("/api/getResponse", function(req, res) {
res.json(req.body);
});
</code></pre>
<p>最后在根目录下的<code>index.html</code>中加上启动该<code>demo</code>的入口:</p>
<pre><code class="language-html"><li><a href="examples/getResponse">getResponse</a></li>
</code></pre>
<p>OK,我们在命令行中执行:</p>
<pre><code class="language-bash"># 同时开启客户端和服务端
npm run server | npm start
</code></pre>
<p>接着我们打开 <code>chrome</code> 浏览器,访问 http://localhost:8000/ 即可访问我们的 <code>demo</code> 了,我们点击 <code>getResponse</code>,通过<code>F12</code>的控制台我们可以看到:两条请求的响应信息都已经被打印出来了,并且第一条请求我们没有指定<code>responseType</code>属性,它默认为<code>text</code>,打印出来的<code>data</code>数据就是字符串类型,而第二条请求我们指定了<code>responseType: "json"</code>,打印出来的<code>data</code>数据就是<code>json</code>类型的。<br>
<img src="https://img2018.cnblogs.com/blog/1460995/201907/1460995-20190727194415895-816784832.gif" alt="" loading="lazy"></p>
<p><img src="https://img2018.cnblogs.com/blog/1460995/201907/1460995-20190727194422971-366130244.jpg" alt="" loading="lazy"></p>
<h1 id="5-遗留问题">5. 遗留问题</h1>
<p>从上图中我们还看到,打印出来的<code>headers</code>变成了字符串类型,并不是我们之前设置的对象类型,而且如果返回的<code>data</code>是一个<code>json</code>字符串,我们还应该给它转换成对象类型的。那么后面我们就来做这两件事情。<br>
(完)</p>
</div>
<div id="MySignature" role="contentinfo">
<br>
<br>
<br>
<h1 style="color: red">免责声明</h1>
<ul>
<li>本博客所有文章仅用于学习、研究和交流目的,欢迎非商业性质转载。</li>
<li>博主在此发文(包括但不限于汉字、拼音、拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入、屏幕显示的机械、光电性能,并不代表本人局部或全部同意、支持或者反对观点。如需要详查请直接与键盘生产厂商法人代表联系。挖井挑水无水表,不会网购无快递。</li>
<li>博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高(其实是个菜B),不足和错误之处在所难免,希望大家能够批评指出。</li>
<li>博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的文章,请原谅博主成为一个无耻的文档搬运工!</li>
</ul><br><br>
来源:https://www.cnblogs.com/wangjiachen666/p/11256456.html
頁:
[1]