使用Typescript重構axios(六)——實現基礎功能:獲取響應數據


0. 系列文章

1.使用Typescript重構axios(一)——寫在最前面
2.使用Typescript重構axios(二)——項目起手,跑通流程
3.使用Typescript重構axios(三)——實現基礎功能:處理get請求url參數
4.使用Typescript重構axios(四)——實現基礎功能:處理post請求參數
5.使用Typescript重構axios(五)——實現基礎功能:處理請求的header
6.使用Typescript重構axios(六)——實現基礎功能:獲取響應數據
7.使用Typescript重構axios(七)——實現基礎功能:處理響應header
8.使用Typescript重構axios(八)——實現基礎功能:處理響應data
9.使用Typescript重構axios(九)——異常處理:基礎版
10.使用Typescript重構axios(十)——異常處理:增強版
11.使用Typescript重構axios(十一)——接口擴展
12.使用Typescript重構axios(十二)——增加參數
13.使用Typescript重構axios(十三)——讓響應數據支持泛型
14.使用Typescript重構axios(十四)——實現攔截器
15.使用Typescript重構axios(十五)——默認配置
16.使用Typescript重構axios(十六)——請求和響應數據配置化
17.使用Typescript重構axios(十七)——增加axios.create
18.使用Typescript重構axios(十八)——請求取消功能:總體思路
19.使用Typescript重構axios(十九)——請求取消功能:實現第二種使用方式
20.使用Typescript重構axios(二十)——請求取消功能:實現第一種使用方式
21.使用Typescript重構axios(二十一)——請求取消功能:添加axios.isCancel接口
22.使用Typescript重構axios(二十二)——請求取消功能:收尾
23.使用Typescript重構axios(二十三)——添加withCredentials屬性
24.使用Typescript重構axios(二十四)——防御XSRF攻擊
25.使用Typescript重構axios(二十五)——文件上傳下載進度監控
26.使用Typescript重構axios(二十六)——添加HTTP授權auth屬性
27.使用Typescript重構axios(二十七)——添加請求狀態碼合法性校驗
28.使用Typescript重構axios(二十八)——自定義序列化請求參數
29.使用Typescript重構axios(二十九)——添加baseURL
30.使用Typescript重構axios(三十)——添加axios.getUri方法
31.使用Typescript重構axios(三十一)——添加axios.all和axios.spread方法
32.使用Typescript重構axios(三十二)——寫在最后面(總結)

項目源碼請猛戳這里!!!

1. 前言

在之前的文章中,不管是get請求還是post請求都已經可以正常發出了,並且在瀏覽器的F12中也都能正確的看到返回的數據了,但這僅僅是在瀏覽器中利用開發者工具看到返回的數據,而在代碼中我們還是無法拿到返回的數據,我們期望的使用方式應該是如下樣子的:

axios({
  method: 'post',
  url: '/api/getResponse',
  data: {
    a: 1,
    b: 2
  }
}).then((res) => {
  console.log(res)
})

我們期望能夠拿到服務端響應的數據,並且支持 Promise 鏈式調用的方式。

2. 響應數據接口定義

我們先來分析下,我們都想要拿到服務端給我們響應哪些數據:首先最主要的服務端返回的數據data不能少,其次,例如HTTP 狀態碼status,狀態消息 statusText,響應頭 headers、請求配置對象 config 以及請求的 XMLHttpRequest 對象實例 request,這些信息我們也都想拿到,基於此,我們先在src/types/index.ts中定義一下服務端響應的數據接口類型AxiosResponse,如下:

export interface AxiosResponse {
  data: any; // 服務端返回的數據
  status: number; // HTTP 狀態碼
  statusText: string; // 狀態消息
  headers: any; // 響應頭
  config: AxiosRequestConfig; // 請求配置對象
  request: any; // 請求的 XMLHttpRequest 對象實例
}

另外,我們還期望axios 函數能夠返回一個 Promise 對象,以滿足我們想要的鏈式調用,那么我們可以定義一個 AxiosPromise 接口,它繼承於 Promise<AxiosResponse> 這個泛型接口:

export interface AxiosPromise extends Promise<AxiosResponse> {
}

這樣的話,當 axios 返回的是 AxiosPromise 類型,那么 resolve 函數中的參數就是一個 AxiosResponse 類型。

對於一個 AJAX 請求的響應,我們在發送請求的時候還可以通過設置 XMLHttpRequest對象的responseType屬性來指定它響應數據的類型responseType (responseType的MDN介紹),於是,我們可以給之前定義好的 AxiosRequestConfig 類型添加一個可選屬性responseType,添加后如下:

export interface AxiosRequestConfig {
  url: string;
  method?: Method;
  headers?: any;
  data?: any;
  params?: any;
  responseType?: XMLHttpRequestResponseType;
}

responseType 的類型是一個 XMLHttpRequestResponseType 類型,它的定義是 "" | "arraybuffer" | "blob" | "document" | "json" | "text" 字符串字面量類型。

3. 獲取響應

定義好響應數據的接口類型后,我們就可以來寫獲取響應的邏輯了。我們知道,一個完整的AJAX流程大致分為4步:

  1. 創建XMLHttpRequest異步對象;

  2. 配置請求參數;

  3. 發送請求;

  4. 注冊事件,獲取響應數據

之前在src/xhr.ts中我們已經完成了前3步,那么接下來我們就實現第4步:

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[name];
      }
      request.setRequestHeader(name, headers[name]);
    });

    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);
    };
  });
}


這里,我們還判斷了如果 config 中配置了 responseType,我們要把它設置到 request.responseType 中。

另外,當 responseType沒有設置或者設置為text時,響應數據存在於request.responseText,其余情況,響應數據存在於request.response,所以我們添加了這行代碼:

const responseData = responseType && responseType !== "text"? request.response: request.responseText;

最后,在 onreadystatechange 事件函數中,我們構造了 AxiosResponse 類型的 reponse 對象,並把它 resolve 出去。

修改了 xhr 函數,我們同樣也要對應修改 axios 函數:

// src/index.ts

function axios(config: AxiosRequestConfig): AxiosPromise {
  processConfig(config)
  return xhr(config)
}

OK,獲取響應就已經完成了,接下來,我們就可以編寫demo來測試下效果怎么樣。

4. 編寫demo

examples 目錄下創建 getResponse目錄,在 getResponse目錄下創建 index.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>

接着再創建 app.ts 作為入口文件:

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);
});

接着在 server/server.js 添加新的接口路由:

router.post("/api/getResponse", function(req, res) {
  res.json(req.body);
});

最后在根目錄下的index.html中加上啟動該demo的入口:

<li><a href="examples/getResponse">getResponse</a></li>

OK,我們在命令行中執行:

# 同時開啟客戶端和服務端
npm run server | npm start

接着我們打開 chrome 瀏覽器,訪問 http://localhost:8000/ 即可訪問我們的 demo 了,我們點擊 getResponse,通過F12的控制台我們可以看到:兩條請求的響應信息都已經被打印出來了,並且第一條請求我們沒有指定responseType屬性,它默認為text,打印出來的data數據就是字符串類型,而第二條請求我們指定了responseType: "json",打印出來的data數據就是json類型的。

5. 遺留問題

從上圖中我們還看到,打印出來的headers變成了字符串類型,並不是我們之前設置的對象類型,而且如果返回的data是一個json字符串,我們還應該給它轉換成對象類型的。那么后面我們就來做這兩件事情。
(完)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM