Fetch API 简单封装

5,773 阅读2分钟

什么是 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
      } 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 的话,把类型和枚举去掉就好,没什么差别