Fetch API簡單封裝

什麼是 Fetch API

Fetch API 提供了一個 JavaScript 接口,用於訪問和操縱 HTTP 管道的部分,例如請求和響應。它還提供了一個全局 fetch()方法,該方法提供了一種簡單,合理的方式來跨網絡異步獲取資源。

爲什麼要使用 Fetch

網絡異步獲取資源之前是用XMLHttpRequest(例如常見的 jquery.ajax(),axios 都是這種)獲取的,Fetch 提供了一個更好的替代方法。現代瀏覽器基本都內置了,不用額外引入,IE 也可以使用 polyfill 等一堆插件解決,其他瀏覽器開箱即用。

爲什麼要封裝 Fetch

  • 當接收到一個代表錯誤的 HTTP 狀態碼時,從 fetch()返回的 Promise 不會被標記爲 reject, 即使該 HTTP 響應的狀態碼是 404 或 500。相反,它會將 Promise 狀態標記爲 resolve (但是會將 resolve 的返回值的 ok 屬性設置爲 false ),僅當網絡故障時或請求被阻止時,纔會標記爲 reject。
  • 默認情況下,fetch 不會從服務端發送或接收任何 cookies, 如果站點依賴於用戶 session,則會導致未經認證的請求(要發送 cookies,必須設置 credentials 選項)。
  • 還有最重要的一點,不用每次都寫一堆代碼。
不瞭解 Fetch 使用方法的這裏提供一下MDN 的教程

簡易代碼 源碼使用了 TypeScript 和 async/await

// fetch-config.ts
import Qs from 'qs'

export enum ContentType {
  json = 'application/json;charset=UTF-8',
  form = 'application/x-www-form-urlencoded; charset=UTF-8'
}

export enum HttpMethod {
  get = 'GET',
  post = 'POST',
  put = 'PUT',
  patch = 'PATCH',
  delete = 'DELETE'
}

export interface IReqConfig {
  body?: any
  method?: string
  headers?: IHeader
  token?: string
  'Content-Type'?: string
}

export interface IHeader {
  'Content-Type': string
  'X-Requested-With': string
  token: string
  [propName: string]: any
}

export const baseUrl = '/'

const $req = async (url: string, config: IReqConfig) => {
  let promise: Response
  let contentType: string
  if (config['Content-Type'] !== undefined) {
    contentType = config['Content-Type']
  } else if (config.method === HttpMethod.post) {
    contentType = ContentType.form
  } else {
    contentType = ContentType.json
  }
  const reqUrl = (baseUrl + url).replace('//', '/')
  const headers: Headers = new Headers({
    // 如果實例配置沒傳token過來的話,直接使用保存在sessionStorage的token
    // 這裏假設後端直接讀頭文件的token字段,我直接用token當字段了,Authorization也同理
    token: config.token === undefined ? sessionStorage.token : config.token,
    'Content-Type': contentType,
    'X-Requested-With': 'XMLHttpRequest'
  } as IHeader)
  if (!config.method || config.method === HttpMethod.get) {
    promise = await fetch(reqUrl, {
      headers
    })
  } else if (config.method === HttpMethod.post) {
    promise = await fetch(reqUrl, {
      body: Qs.stringify(config.body),
      headers,
      method: HttpMethod.post
    })
  } else {
    promise = await fetch(reqUrl, {
      body: JSON.stringify(config.body),
      headers,
      method: config.method
    })
  }
  return handleRes(promise)
}

const handleRes = async (res: Response) => {
  const parsedRes = await parseRes(res)
  // 如果res.ok,則請求成功
  if (res.ok) {
    return parsedRes
  }
  // 請求失敗,返回解析之後的失敗的數據
  const error = parsedRes
  throw error
}

const parseRes = async (res: Response) => {
  const contentType = res.headers.get('Content-Type')
  // 判定返回的內容類型,做不同的處理
  if (contentType) {
    if (contentType.indexOf('json') > -1) {
      return await res.json()
    }
    if (contentType.indexOf('text') > -1) {
      return await res.text()
    }
    if (contentType.indexOf('form') > -1) {
      return await res.formData()
    }
    if (contentType.indexOf('video') > -1) {
      return await res.blob()
    }
  }
  return await res.text()
}

export default $req

使用的時候

// test.ts
import $req, { HttpMethod } from './fetch-config'

async function getMethod() {
  const getUrl: string = 'XXXX'
  try {
    const res: any = await $req(getUrl)
  } catch(e) {
    // 處理異常
  }
}

async function postMethod() {
  const postUrl: string = 'XXXX'
  const reqBody: any = {/* request body */}
  try {
    const res: any = await $req(postUrl, {
      body: reqBody,
      method: HttpMethod.post
    })
  } catch(e) {
    // 處理異常
  }
}

async function patchMethod() {
  const patchUrl: string = 'XXXX'
  const reqBody: any = {/* request body */}
  try {
    const res: any = await $req(patchUrl, {
      body: reqBody,
      method: HttpMethod.patch
    })
  } catch(e) {
    // 處理異常
  }
}

以上就是 fetch API 的簡單封裝,如果用的 JavaScript 的話,把類型和枚舉去掉就好,沒什麼差別

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章