@@ -615,67 +615,99 @@ CORS机制与JSONP模式的使用目的相同,而且更强大。JSONP只支持
615615
616616## Fetch API
617617
618- Fetch API是一种新兴的规范,用来操作浏览器发出的网络请求,目的是在将来取代XMLHttpRequest。它的作用与XMLHttpRequest类似,但是返回的是Promise对象,因此API大大简化,并且避免了嵌套的回调函数。
618+ ### 基本用法
619+
620+ Ajax操作所用的XMLHttpRequest对象,已经有十多年的历史,它的API设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。Fetch API是一种新规范,用来取代XMLHttpRequest对象。它主要有两个特点,一是简化接口,将API分散在几个不同的对象上,二是返回Promise对象,避免了嵌套的回调函数。
621+
622+ 检查浏览器是否部署了这个API的代码如下。
623+
624+ ``` javascript
625+
626+ if (fetch in window ){
627+ // 支持
628+ } else {
629+ // 不支持
630+ }
631+
632+ ```
619633
620634下面是一个XMLHttpRequest对象发出Ajax请求的常规例子。
621635
622636``` javascript
623637
624- function reqListener () {
625- var data = JSON .parse (this .responseText );
626- console .log (data);
638+ function reqListener () {
639+ var data = JSON .parse (this .responseText );
640+ console .log (data);
627641}
628642
629- function reqError (err ) {
630- console .log (' Fetch Error :-S' , err);
643+ function reqError (err ) {
644+ console .log (' Fetch Error :-S' , err);
631645}
632646
633- var oReq = new XMLHttpRequest ();
634- oReq .onload = reqListener;
635- oReq .onerror = reqError;
636- oReq .open (' get' , ' ./api/some.json' , true );
647+ var oReq = new XMLHttpRequest ();
648+ oReq .onload = reqListener;
649+ oReq .onerror = reqError;
650+ oReq .open (' get' , ' ./api/some.json' , true );
637651oReq .send ();
638652
639653```
640654
641- 上面的例子用Fetch实现如下 。
655+ 同样的操作用Fetch实现如下 。
642656
643657``` javascript
644658
645- fetch (' ./api/some.json' )
646- .then (function (response ) {
647- if (response .status !== 200 ) {
648- console .log (' 请求失败,状态码:' + response .status );
649- return ;
650- }
651- response .json ().then (function (data ) {
652- console .log (data);
653- });
654- }).catch (function (err ) {
655- console .log (' 出错:' , err);
659+ fetch (' ./api/some.json' )
660+ .then (function (response ) {
661+ if (response .status !== 200 ) {
662+ console .log (' 请求失败,状态码:' + response .status );
663+ return ;
664+ }
665+ response .json ().then (function (data ) {
666+ console .log (data);
667+ });
668+ }).catch (function (err ) {
669+ console .log (' 出错:' , err);
656670 });
657671
658- ```
672+ ```
659673
660674上面代码中,因为HTTP请求返回的response对象是一个Stream对象,使用json方法转为JSON格式,但是json方法返回的是一个Promise对象。
661675
676+ fetch函数的第一个参数可以是URL字符串,也可以是后文要讲到的Request对象实例。
677+
678+ response对象还有一个ok属性,如果返回的状态码在200到299之间(即请求成功),这个属性为true,否则为false。因此,上面的代码可以写成下面这样。
679+
680+ ``` javascript
681+ fetch (" ./api/some.json" ).then (function (response ) {
682+ if (response .ok ) {
683+ response .json ().then (function (data ) {
684+ console .log (data);
685+ });
686+ } else {
687+ console .log (" 请求失败,状态码为" , response .status );
688+ }
689+ }, function (err ) {
690+ console .log (" 出错:" , err);
691+ });
692+ ```
693+
662694response对象除了json方法,还包含了HTTP回应的元数据。
663695
664696``` javascript
665697
666- fetch (' users.json' ).then (function (response ) {
667- console .log (response .headers .get (' Content-Type' ));
698+ fetch (' users.json' ).then (function (response ) {
699+ console .log (response .headers .get (' Content-Type' ));
668700 console .log (response .headers .get (' Date' ));
669701
670- console .log (response .status );
671- console .log (response .statusText );
672- console .log (response .type );
673- console .log (response .url );
702+ console .log (response .status );
703+ console .log (response .statusText );
704+ console .log (response .type );
705+ console .log (response .url );
674706});
675707
676708```
677709
678- 上面代码中,response.type表示HTTP回应的类型 ,它有以下三个值。
710+ 上面代码中,response对象有很多属性,其中的type属性比较特别,表示HTTP回应的类型 ,它有以下三个值。
679711
680712- basic:正常的同域请求
681713- cors:CORS机制下的跨域请求
@@ -702,8 +734,8 @@ fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
702734
703735``` javascript
704736
705- fetch (url, {
706- credentials: ' include'
737+ fetch (url, {
738+ credentials: ' include'
707739})
708740
709741```
@@ -712,21 +744,206 @@ fetch(url, {
712744
713745``` javascript
714746
715- fetch (url, {
716- method: ' post' ,
717- headers: {
718- " Content-type" : " application/x-www-form-urlencoded; charset=UTF-8"
719- },
720- body: ' foo=bar&lorem=ipsum'
721- })
722- .then (json)
723- .then (function (data ) {
724- console .log (' Request succeeded with JSON response' , data);
725- })
726- .catch (function (error ) {
727- console .log (' Request failed' , error);
747+ fetch (" http://www.example.org/submit.php" , {
748+ method: " POST" ,
749+ headers: {
750+ " Content-Type" : " application/x-www-form-urlencoded"
751+ },
752+ body: " firstName=Nikhil&favColor=blue&password=easytoguess"
753+ }).then (function (res ) {
754+ if (res .ok ) {
755+ console .log (" Perfect! Your settings are saved." );
756+ } else if (res .status == 401 ) {
757+ console .log (" Oops! You are not authorized." );
758+ }
759+ }, function (e ) {
760+ console .log (" Error submitting form!" );
761+ });
762+
763+ ```
764+
765+ 目前,还有一些XMLHttpRequest对象可以做到,但是Fetch API还没做到的地方,比如中途中断HTTP请求,以及获取HTTP请求的进度。这些不足与Fetch返回的是Promise对象有关。
766+
767+ ### Headers
768+
769+ Fetch API引入三个新的对象(也是构造函数):Headers, Request 和 Response。其中,Headers对象用来构造/读取HTTP数据包的头信息。
770+
771+ ``` javascript
772+ var content = " Hello World" ;
773+ var reqHeaders = new Headers ();
774+ reqHeaders .append (" Content-Type" , " text/plain" );
775+ reqHeaders .append (" Content-Length" , content .length .toString ());
776+ reqHeaders .append (" X-Custom-Header" , " ProcessThisImmediately" );
777+ ```
778+
779+ Headers对象的实例,除了使用append方法添加属性,也可以直接通过构造函数一次性生成。
780+
781+ ``` javascript
782+ reqHeaders = new Headers ({
783+ " Content-Type" : " text/plain" ,
784+ " Content-Length" : content .length .toString (),
785+ " X-Custom-Header" : " ProcessThisImmediately" ,
786+ });
787+ ```
788+
789+ Headers对象实例还提供了一些工具方法。
790+
791+ ``` javascript
792+ reqHeaders .has (" Content-Type" ) // true
793+ reqHeaders .has (" Set-Cookie" ) // false
794+ reqHeaders .set (" Content-Type" , " text/html" )
795+ reqHeaders .append (" X-Custom-Header" , " AnotherValue" )
796+
797+ reqHeaders .get (" Content-Length" ) // 11
798+ reqHeaders .getAll (" X-Custom-Header" ) // ["ProcessThisImmediately", "AnotherValue"]
799+
800+ reqHeaders .delete (" X-Custom-Header" )
801+ reqHeaders .getAll (" X-Custom-Header" ) // []
802+ ```
803+
804+ ### Request
805+
806+ Request对象用来构造HTTP请求。
807+
808+ ``` javascript
809+ var req = new Request (" /index.html" );
810+ req .method // "GET"
811+ req .url // "http://example.com/index.html"
812+ ```
813+
814+ Request对象实例的非url属性,只能通过Request构造函数的第二个参数进行设置。
815+
816+ ``` javascript
817+ var uploadReq = new Request (" /uploadImage" , {
818+ method: " POST" ,
819+ headers: {
820+ " Content-Type" : " image/png" ,
821+ },
822+ body: " image data"
823+ });
824+ ```
825+
826+ Request对象实例有两个属性是只读的,不能手动设置。一个是referrer属性,表示请求的来源,由浏览器设置,有可能是空字符串。另一个是context属性,表示请求发出的上下文,如果是image,表示是从img标签发出,如果是worker,表示是从worker脚本发出,如果是fetch,表示是从fetch函数发出的。
827+
828+ Request对象实例的mode属性,用来设置是否跨域,合法的值有以下三种:same-origin、no-cors(默认值)、cors。当设置为same-origin时,只能向同域的URL发出请求,否则会报错。
829+
830+ ``` javascript
831+ var arbitraryUrl = document .getElementById (" url-input" ).value ;
832+ fetch (arbitraryUrl, { mode: " same-origin" }).then (function (res ) {
833+ console .log (" Response succeeded?" , res .ok );
834+ }, function (e ) {
835+ console .log (" Please enter a same-origin URL!" );
836+ });
837+ ```
838+
839+ 上面代码中,如果用户输入的URL不是同域的,将会报错,否则就会发出请求。
840+
841+ 如果mode属性为no-cors,就与默认的浏览器行为没有不同,类似script标签加载外部脚本文件、img标签加载外部图片。如果mode属性为cors,就可以向部署了CORS机制的服务器,发出跨域请求。
842+
843+ ``` javascript
844+ var u = new URLSearchParams ();
845+ u .append (' method' , ' flickr.interestingness.getList' );
846+ u .append (' api_key' , ' <insert api key here>' );
847+ u .append (' format' , ' json' );
848+ u .append (' nojsoncallback' , ' 1' );
849+
850+ var apiCall = fetch (' https://api.flickr.com/services/rest?' + u);
851+
852+ apiCall .then (function (response ) {
853+ return response .json ().then (function (json ) {
854+ // photo is a list of photos.
855+ return json .photos .photo ;
856+ });
857+ }).then (function (photos ) {
858+ photos .forEach (function (photo ) {
859+ console .log (photo .title );
728860 });
861+ });
862+ ```
863+
864+ 上面代码是向Flickr API发出图片请求的例子。
865+
866+ ### Response
867+
868+ fetch方法返回Response对象实例,它有以下属性。
869+
870+ - status:整数值,表示状态码(比如200)
871+ - statusText:字符串,表示状态信息,默认是“OK”
872+ - ok:布尔值,表示状态码是否在200-299的范围内
873+ - headers:Headers对象,表示HTTP回应的头信息
874+ - url:字符串,表示HTTP请求的网址
875+ - type:字符串,合法的值有五个basic、cors、default、error、opaque。basic表示正常的同域请求;cors表示CORS机制的跨域请求;error表示网络出错,无法取得信息,status属性为0,headers属性为空,并且导致fetch函数返回Promise对象被拒绝;opaque表示非CORS机制的跨域请求,受到严格限制。
876+
877+ Response对象还有两个静态方法。
878+
879+ - Response.error() 返回一个type属性为error的Response对象实例
880+ - Response.redirect(url, status) 返回的Response对象实例会重定向到另一个URL
881+
882+ ### body属性
729883
884+ Request对象和Response对象都有body属性,表示请求的内容。body属性可能是以下的数据类型。
885+
886+ - ArrayBuffer
887+ - ArrayBufferView (Uint8Array等)
888+ - Blob/File
889+ - string
890+ - URLSearchParams
891+ - FormData
892+
893+ ``` javascript
894+ var form = new FormData (document .getElementById (' login-form' ));
895+ fetch (" /login" , {
896+ method: " POST" ,
897+ body: form
898+ })
899+ ```
900+
901+ 上面代码中,Request对象的body属性为表单数据。
902+
903+ Request对象和Response对象都提供以下方法,用来读取body。
904+
905+ - arrayBuffer()
906+ - blob()
907+ - json()
908+ - text()
909+ - formData()
910+
911+ 注意,上面这些方法都只能使用一次,第二次使用就会报错,也就是说,body属性只能读取一次。Request对象和Response对象都有bodyUsed属性,返回一个布尔值,表示body是否被读取过。
912+
913+ ``` javascript
914+ var res = new Response (" one time use" );
915+ console .log (res .bodyUsed ); // false
916+ res .text ().then (function (v ) {
917+ console .log (res .bodyUsed ); // true
918+ });
919+ console .log (res .bodyUsed ); // true
920+
921+ res .text ().catch (function (e ) {
922+ console .log (" Tried to read already consumed Response" );
923+ });
924+ ```
925+
926+ 上面代码中,第二次通过text方法读取Response对象实例的body时,就会报错。
927+
928+ 这是因为body属性是一个stream对象,数据只能单向传送一次。这样的设计是为了允许JavaScript处理视频、音频这样的大型文件。
929+
930+ 如果希望多次使用body属性,可以使用Response对象和Request对象的clone方法。它必须在body还没有读取前调用,返回一个前的body,也就是说,需要使用几次body,就要调用几次clone方法。
931+
932+ ``` javascript
933+ addEventListener (' fetch' , function (evt ) {
934+ var sheep = new Response (" Dolly" );
935+ console .log (sheep .bodyUsed ); // false
936+ var clone = sheep .clone ();
937+ console .log (clone .bodyUsed ); // false
938+
939+ clone .text ();
940+ console .log (sheep .bodyUsed ); // false
941+ console .log (clone .bodyUsed ); // true
942+
943+ evt .respondWith (cache .add (sheep .clone ()).then (function (e ) {
944+ return sheep;
945+ });
946+ });
730947` ` `
731948
732949## 参考链接
@@ -738,3 +955,4 @@ fetch(url, {
738955- Monsur Hossain, [Using CORS](http://www.html5rocks.com/en/tutorials/cors/)
739956- MDN, [HTTP access control (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS)
740957- Matt Gaunt, [Introduction to fetch()](http://updates.html5rocks.com/2015/03/introduction-to-fetch)
958+ - Nikhil Marathe, [This API is so Fetching!](https://hacks.mozilla.org/2015/03/this-api-is-so-fetching/)
0 commit comments