Fetch的使用

原文链接:https://www.cnblogs.com/libin-1/p/6853677.html

无论用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

浏览器支持

桌面浏览器



手机/平板电脑



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

Fetch获取数据

使用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返回的响应能告知请求的状态。从上面例子看出请求是成功的(ok是true,status是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就是我们请求的reposconsole.log(data)

  });

可以看出Fetch获取数据方法简短并且简单。

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

Fetch发送数据

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

fetch('some-url', options);

第一个参数是设置请求方法(如post、put或del),Fetch会自动设置方法为get。

第二个参数是设置头部。因为一般使用JSON数据格式,所以设置ContentType为application/json。

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

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

let content = {some:'content'};

// The actual

fetch requestfetch('some-url', {

    method:'post',

    headers: {

        'Content-Type':'application/json' 

    },

    body:JSON.stringify(content)

})

// .then()...

Fetch处理异常

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

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

没有权限获取资源

输入参数有误

服务器抛出异常

服务器超时

服务器崩溃

API更改

...

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

// 获取chrissycoyier's repos 而不是 chriscoyier's reposfetch('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"

}

大部分是一样的,只有ok、status和statusText是不同的,正如所料,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 {

    thrownewError('something went wrong!')

}

// rejecting a Promise

else {

    returnPromise.reject('something went wrong!')

}

这里选择Promise.reject,是因为容易扩展。抛出异常方法也不错,但是无法扩展,唯一益处在于便于栈跟踪。

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

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

    .then(response => {

        if (response.ok) {

            return response.json()   

        }else {

            returnPromise.reject('something went wrong!')   

        } 

}) 

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

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


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


但是rejectPromise时,只输出字符串不太好。这样不清楚哪里出错了,你肯定也不会想在异常时,输出下面这样:


让我们在看看响应:

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

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

上面的错误处理方法对于下面这些不需要解释的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读取,然后决定怎么处理。


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

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

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

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


处理其他响应类型

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

至于其他的10%呢?

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


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


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

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



介绍zlFetch

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

典型的zlfetch像下面这样:

使用之前,需要安装zlFetch

npm install zl-fetch --save

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


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

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


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

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

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


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

总结

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

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

使用zlFetch库,就不需要担心错误处理了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,596评论 18 139
  • 现在应该很少有人用原生的JS内置XMLHttpRequest对象写异步调用了,仍然用的比较多的应该是Jquery的...
    琉璃橙子阅读 1,866评论 0 2
  • fetch是js提供进行网络请求的框架。 调用结构是这样的。 fetch( url , options ).the...
    lzh_coder阅读 477评论 0 0
  • fetch已经被大部分的浏览器兼容了,包括chrome,Firefox,safari,opera,edge,但是I...
    风吹过的空气阅读 882评论 0 0
  • 开挂人生 一个有趣的事情就是娜姐邀请到DISC李海峰老师来成都分享,李海峰老师顺带把简书一哥六哥,还有BM创始人敏...
    深井冰就是大神阅读 116评论 0 0