Fetch的使用

转载文章:使用 Fetchhtml

不管用JavaScript发送或获取信息,咱们都会用到Ajax。Ajax不须要刷新页面就能发送和获取信息,能使网页实现异步更新。node

几年前,初始化Ajax通常使用jQuery的ajax方法:git

$.ajax('some-url', { success: (data) => { /* do something with the data */ }, error: (err) => { /* do something when an error happens */} });

也能够不用jQuery,但不得不使用XMLHttpRequest,然而这是至关复杂github

幸好,浏览器如今支持Fetch API,能够无须其余库就能实现Ajaxweb

浏览器支持

全部主要的浏览器(除了Opera Mini和老的IE)都支持Fetch。针对不支持的,可使用Fetch polyfillajax

Fetch获取数据

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

假设咱们想经过GitHub获取一个仓库,咱们能够像下面这样使用:npm

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

Fetch会返回Promise,因此在获取资源后,可使用.then方法作你想作的。json

fetch('https://api.github.com/users/chriscoyier/repos') .then(response => {/* do something */})

若是这是你第一次碰见Fetch,也许惊讶于Fetch返回的response。若是console.log返回的response,会获得下列信息:api

{ 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返回的响应能告知请求的状态。从上面例子看出请求是成功的(oktruestatus是200),可是咱们想获取的仓库名却不在这里。

显然,咱们从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发送数据。

Fetch发送数据

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

fetch('some-url', options);

第一个参数是设置请求方法(如postputdel),Fetch会自动设置方法为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()...

 

Fetch处理异常

虽然但愿Ajax响应成功,可是仍会有问题出现:

  1. 可能尝试获取不存在的资源
  2. 没有权限获取资源
  3. 输入参数有误
  4. 服务器抛出异常
  5. 服务器超时
  6. 服务器崩溃
  7. API更改
  8. ...

假设咱们试图获取不存在错误,并了解如何处理错误。下面的例子我将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方法会执行。

 

若是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 Error
else { throw new Error('something went wrong!') } // rejecting a Promise
else { 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语句中。

 

可是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: Unauthorized
  • 404: Not found
  • 408: Connection timeout
  • ...

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

  • 400:Bad request
    例如,若是请求错误缺乏必要的参数,就会返回400.

    光在catch中告诉状态及缘由短语并不足够。咱们须要知道缺乏什么参数。
    因此服务器须要返回一个对象,告诉形成错误请求缘由。若是使用Node和Express,会返回像下面这样的响应:
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))

 

处理其余响应类型

到如今,咱们只处理JSON格式的响应,而返回JSON格式数据大约占90%。

至于其余的10%呢?

假设上面的例子返回的是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 Imports
import zlFetch from 'zl-fetch'; // CommonJS Imports
const 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});

 

下面就是使用zlFetch来从GitHub上获取repos:

 超时

Promise.race()方法,写一个超时的错误方法,好比说3000ms以后抛错。

总结

Fetch是很好的方法,能发送和接收数据。不须要在编写XHR请求或依赖于jQuery。

尽管Fetch很好,可是其错误处理不是很直接。在处理以前,须要让错误信息进入到catch方法中。

使用zlFetch库,就不须要担忧错误处理了。

相关文章
相关标签/搜索