新睿云

> 知识库 > jQuery使用fetch详解

jQuery使用fetch详解

作者/来源:新睿云小编 发布时间:2020-01-19

无论使用JavaScript发送或获取信息,我们都会用到Ajax。Ajax不需要刷新页面即可发送和获取信息,可以使网页实现反向更新。

几年前,初始化Ajax一般使用jQuery的ajax方法:

$.ajax('some-url', {

  success: (data) => { /* do something with the data */ },

  error: (err) => { /* do something when an error happens */}

});

也可以不用jQuery,但不得不使用XMLHttpRequest,然而这是相当复杂

幸亏,浏览器现在支持Fetch API,可以无须其他库就能实现Ajax

一、jQuery使用fetch详解

1、浏览器支持

1.1桌面浏览器

1.2手机/平板电脑

所有主要的浏览器(除了Opera Mini和老的IE)都支持Fetch。针对不支持的,可以使用Fetch polyfill

2、获取数据

使用Fetch获取数据很容易。只需要Fetch你想获取资源。

假设我们想通过GitHub获取一个仓库,我们可以像下面这样使用:

fetch('https://api.github.com/users/chriscoyier/repos');

Fetch会返回Promise,所以在获取资源后,可以使用.then方法做你想做的。

fetch('https://api.github.com/users/chriscoyier/repos')

  .then(response => {/* do something */})

如果这是你第一次遇见Fetch,也许某些于Fetch返回的response。如果console.log返回的response,会得到以下信息:

{

  body: ReadableStream

  bodyUsed: false

  headers: Headers

  ok : true

  redirected : false

  status : 200

  statusText : "OK"

  type : "cors"

  url : "http://some-website.com/some-url"

  __proto__ : Response

}

可以修剪Fetch返回的响应能量通知请求的状态。从上面示例修剪请求是成功的(oktruestatus200),但是我们想获取的仓库名却不在这里。

显然,我们从GitHub请求的资源都存储在body中,作为一种提示的流。所以需要调用一个适当的方法将逐步流转换为我们可以使用的数据。

Github返回的响应是JSON格式的,所以调用response.json方法来转换数据。

还有其他方法来处理不同类型的响应。如果请求一个XML格式文件,则调用response.text。如果请求图片,使用response.blob方法。

所有这些方法(response.json等等)返回另一个Promise,所以可以调用.then方法处理我们转换后的数据。

fetch('https://api.github.com/users/chriscoyier/repos')

  .then(response => response.json())

  .then(data => {

    // data就是我们请求的repos

    console.log(data)

  });

可以抛光Fetch获取数据方法替换和简单。

接下来,让我们看看如何使用Fetch发送数据。

3、获取发送数据

使用Fetch发送也很简单,只需要配置三个参数。

fetch('some-url', options);

第一个参数是设置请求方法(如postputdel),获取会自动设置方法为get

因为通常使用JSON数据格式,所以设置ContentTypeapplication/json

第三个参数是设置包含JSON内容的主体。因为JSON内容是必须的,所以当设置主体时会调用JSON.stringify

实践中,post请求会像下面这样:

let content = {some: 'content'};// The actual fetch request

fetch('some-url', {

  method: 'post',

  headers: {

    'Content-Type': 'application/json'

  },

  body: JSON.stringify(content)

})// .then()...

4、提取处理异常

虽然希望Ajax响应成功,但仍会有问题出现:

4.1可能尝试获取不存在的资源

4.2没有权限获取资源

4.3输入参数有误

4.4服务器抛出异常

4.4.1服务器超时

4.4.2服务器崩溃

4.4.3API更改

假设我们试图获取不存在错误,并了解如何处理错误。下面的例子我将chriscoyier拼错为chrissycoyier

// 获取chrissycoyier's repos 而不是 chriscoyier's repos

fetch('https://api.github.com/users/chrissycoyier/repos')

为了处理此错误,我们需要使用catch方法。

也许我们会用以下这种方法:

fetch('https://api.github.com/users/chrissycoyier/repos')

  .then(response => response.json())

  .then(data => console.log('data is', data))

  .catch(error => console.log('error is', error));

然而却得到以下这样的结果:

获取失败,但是第二个.then方法会执行

获取失败,但是第二个.then方法会执行。

如果console.log此次响应,会抛光不同:

  {

  body: ReadableStream

  bodyUsed: true

  headers: Headers

  ok: false // Response is not ok

  redirected: false

  status: 404 // HTTP status is 404.

  statusText: "Not Found" // Request not found

  type: "cors"

  url: "https://api.github.com/users/chrissycoyier/repos"

}

大部分是一样的,只有okstatusstatusText是不同的,先前所料,GitHub上没有发现chrissycoyier

上面的响应告诉我们Fetch不会关心AJAX是否成功,他只关心从服务器发送请求和接收响应,如果响应失败我们需要抛出异常。

因此,初始的then方法需要被重写,以至于如果响应成功会调用response.json。最简单方法是检查response是否为ok

fetch('some-url')

  .then(response => {

    if (response.ok) {

      return response.json()

    } else {

      // Find some way to get to execute .catch()

    }

  });

一旦我们知道请求是不成功的,我可以throw异常或rejectPromise来调用catch

// throwing an Errorelse {

  throw new Error('something went wrong!')

}// rejecting a Promiseelse {

  return Promise.reject('something went wrong!')

}

这里选择Promise.reject,是因为容易扩展。抛出异常方法也不错,但是无法扩展,唯一可行且可以堆栈跟踪。

所以,到现在代码应该是这样的:

fetch('https://api.github.com/users/chrissycoyier/repos')

  .then(response => {

    if (response.ok) {

      return response.json()

    } else {

      return Promise.reject('something went wrong!')

    }

  })

  .then(data => console.log('data is', data))

  .catch(error => console.log('error is', error));

这样错误就会进入catch语句中

这样错误就会进入catch语句中

但是rejectPromise时,只输出串行不太好。这样明显哪里出错了,你肯定也不会想在异常时,输出以下这样:

响应 

响应

让我们在看看响应:

  {

  body: ReadableStream

  bodyUsed: true

  headers: Headers

  ok: false // Response is not ok

  redirected: false

  status: 404 // HTTP status is 404.

  statusText: "Not Found" // Request not found

  type: "cors"

  url: "https://api.github.com/users/chrissycoyier/repos"

}

在我们这个例子中,我们知道资源是不存在。所以我们可以返回404状态或Not Found原因导致,而我们就知道如何处理。

为了在.catch中获取statusstatusText,我们可以reject一个JavaScript对象:

fetch('some-url')

  .then(response => {

    if (response.ok) {

      return response.json()

    } else {

      return Promise.reject({

        status: response.status,

        statusText: response.statusText

      })

    }

  })

  .catch(error => {

    if (error.status === 404) {

      // do something about 404

    }

  })

上面的错误处理方法对于以下这些不需要的HTTP状态很适用。

401:未经授权

404:找不到

408:连接超时

...

但对于下面这些特定的错误不适用:

400:错误请求,
例如,如果请求错误的必要参数,就会返回400。

400

400

在光catch中告诉状态及原因【搭配模式】并不足够我们需要知道缺少什么参数。
所以服务器需要返回一个对象,告诉造成错误请求原因如果使用节点和快递,会返回像下面这样的响应:

res.status(400).send({

  err: 'no first name'

})

无法在最初的.then方法中reject,因为错误对象需要response.json来解析。
解决的方法是需要两个then方法。这样可以首先通过response.json读取,然后决定如何处理。

fetch('some-error')

  .then(handleResponse)function handleResponse(response) {

  return response.json()

    .then(json => {

      if (response.ok) {

        return json

      } else {

        return Promise.reject(json)

      }

    })

}

首先我们调用response.json读取服务器发来的JSON数据,response.json返回Promise,所以可以链式调用.then方法。

在第一个.then中调用第二个.then,因为我们仍希望通过repsonse.ok判断响应是否成功。

如果想发送状态和原因而言,可以使用Object.assign()将两者结合为一个对象。

let error = Object.assign({}, json, {

  status: response.status,

  statusText: response.statusText

})return Promise.reject(error)

可以使用这样的新的handleResponse函数,让数据能自动的进入.then.catch中。

fetch('some-url')

  .then(handleResponse)

  .then(data => console.log(data))

  .catch(error => console.log(error))

5、处理其他响应类型

到现在,我们只处理JSON格式的响应,而返回JSON格式数据大约占90%。

还有其他的10%呢?

假设上面的例子返回的是XML格式的响应,也许会收到以下异常:

XML 

XML

这是因为XML格式不是JSON格式,我们无法使用response.json,事实上,我们需要response.text,所以我们需要通过判断响应的头部来决定内容格式:

.then(response => {

  let contentType = response.headers.get('content-type')

  if (contentType.includes('application/json')) {

    return response.json()

    // ...

  }

  else if (contentType.includes('text/html')) {

    return response.text()

    // ...

  }

  else {

    // Handle other responses accordingly...

  }

});

当我遇见这种问题时,我尝试使用ExpressJWT处理身份验证,我不知道可以发生JSON响应数据,所以我将XML格式设置为。

这是我们到现在完整代码:

fetch('some-url')

  .then(handleResponse)

  .then(data => console.log(data))

  .then(error => console.log(error))function handleResponse (response) {

  let contentType = response.headers.get('content-type')

  if (contentType.includes('application/json')) {

    return handleJSONResponse(response)

  } else if (contentType.includes('text/html')) {

    return handleTextResponse(response)

  } else {

    // Other response types as necessary. I haven't found a need for them yet though.

    throw new Error(`Sorry, content-type ${contentType} not supported`)

  }

}function handleJSONResponse (response) {

  return response.json()

    .then(json => {

      if (response.ok) {

        return json

      } else {

        return Promise.reject(Object.assign({}, json, {

          status: response.status,

          statusText: response.statusText

        }))

      }

    })

}function handleTextResponse (response) {

  return response.text()

    .then(text => {

      if (response.ok) {

        return json

      } else {

        return Promise.reject({

          status: response.status,

          statusText: response.statusText,

          err: text

        })

      }

    })

}

二、介绍zlFetch

zlFetch库就是上例中handleResponse函数,所以可以不用生成此函数,不需要担心响应来处理数据和错误。

典型的zlfetch像下面这样:

zlFetch('some-url', options)

  .then(data => console.log(data))

  .catch(error => console.log(error));

使用之前,需要安装zlFetch

 npm install zl-fetch --save

接着,需要约会到你的代码中,如果你需要polyfill,确保加入zlFetch之前约会它。

 // Polyfills (if needed)require('isomorphic-fetch') // or whatwg-fetch or node-fetch if you prefer// ES6 Importsimport zlFetch from 'zl-fetch';// CommonJS Importsconst zlFetch = require('zl-fetch');

zlFetch还能无须转换成JSON格式可以发送JSON数据。

下面两个函数确实同样事情,zlFetch加入Content-type然后将内容转换为JSON格式。

let content = {some: 'content'}// Post request with fetch

fetch('some-url', {

  method: 'post',

  headers: {'Content-Type': 'application/json'}

  body: JSON.stringify(content)

});// Post request with zlFetch

zlFetch('some-url', {

  method: 'post',

  body: content

});

zlFetch处理身份认证也很容易。

常用方法是在头部加入Authorization,其值设为Bearer your-token-here。你如果需要增加token选项对话,zlFetch会帮你创建此域。

所以,下面两种代码是一样的:

let token = 'someToken'

zlFetch('some-url', {

  headers: {

    Authorization: `Bearer ${token}`

  }

});// Authentication with JSON Web Tokens with zlFetch

zlFetch('some-url', {token});

热门标签
new year
在线咨询
咨询热线 400-0505-565
投诉与建议
{{item.description}}

—您的烦恼我们已经收到—

我们会将处理结果发送至您的手机

请耐心等待