Fetch API的基本使用

554

为什么要使用fetch?

  1. 传统的 Ajax请求使用的是XMLHttpRequest,XMLHttpRequest 是一个设计粗糙的 API,不符合关注分离(Separation of Concerns)的原则,配置和调用方式非常混乱
  2. XMLHttpRequest采用的是传统的事件驱动模式,无法适配新的 Promise Api

而Fetch 的出现就是为了解决 XHR 的问题

Fetch Api有什么特点?

  1. 首先要理解的一点是,Fetch并非取代AJAX,而是对AJAX传统API的改进。
  2. 精细的功能分割:头部信息,请求信息,响应信息等均分布到不同的对象,更利于处理各种复杂的AJAX场景
  3. 使用Promise Api,更利于异步代码的书写。
  4. Fetch Api 属于HTML5新增的web api,并不属于ES官方

如何使用Fetch?

Fetch的使用很简单,调用fetch函数即可立即向服务器发送网络请求,该函数有两个参数:

  1. 请求地址(必填,字符串)
  2. 请求配置对象(选填,对象)

请求配置对象

  • method: 请求方法【字符串】,默认GET
  • headers: 请求头信息【对象】
  • body: 请求体的内容,必须匹配请求头中的 Content-Type
  • mode: 请求模式【字符串】
    • cors: 默认值。配置为该值,会在请求头中加入origin和referer
    • no-cors: 配置为该值,不会再请求头中加入origin和referer
    • same-origin: 请求必须在同一个域中发生,如果请求其他域,则会报错
  • credentails: 如何携带凭据(cookie)
    • omit: 默认值,不携带cookie
    • same-origin: 请求同源地址时携带cookie
    • include: 请求任何地址都携带cookie
  • cache: 配置缓存模式
    • default: 表示fetch请求之前都将检查下http的缓存
    • no-store: 表示fetch请求将完全忽略http缓存的存在,这意味着请求之前将不在检查http的缓存,拿到缓存后,也会不跟新http缓存
    • no-cache: 如果存在缓存,那么fetch将发送一个条件查询request和一个正常的request,拿到响应后,它会更新http缓存
    • reload: 表示fetch请求之前将忽略http缓存的存在,但是请求拿到响应后,它将主动更新http缓存。
    • force-cache: 表示fetch请求不顾一切的依赖缓存,即使缓存过期了,它依然从缓存中读取,除非没有任何缓存,那么它将发送一个正常的request
    • only-if-cached: 表示fetch请求不顾一切的依赖缓存,即使缓存过期了,它依然从缓存中读取,如果没有缓存,它将抛出网络错误(该设置只在mode为"same-origin"时有效)

返回值

fetch函数返回一个Promise对象

  • 当收到服务器的返回结果后,Promise进入resolved状态,状态数据为Response对象
  • 当网络发生错误(或其他带至无法完成交互的错误)时,Promise进入rejected状态,状态数据为错误信息

Response对象

  • ok: boolean,当响应消息码在200-299之间时为true,其他为false
  • status: number,响应的状态码
  • text(): 用于处理文本格式的Ajax响应,它从响应中获取文本流,将其读完,然后返回一个被解决为String对象的Promise
  • blob(): 用于处理二进制文件格式(比如图片或电子表格)的Ajax响应。它读取文件的原始数据,一旦读取完整个文件,就返回一个被解决为blob对象的Promise
  • json(): 用于处理JSON格式的Ajax的响应。它将JSON数据流转换为一个被解决为JavaScript对象的Promise
  • redirect(): 可以用于重定向要另一个URL,比如服务器返回的是一个地址,则可以利用该方法直接请求该地址,它会创建一个新的Promise,以解决来自重定向的URL的响应。

例子:

<button>得到所有的省份数据</button>
<script>
    async function getProvince(){
        const url = 'http://101.132.72.36:5100/api/local'
        try{
            const resData = await fetch(url);   //得到一个response对象
            const result = await resData.json();    //得到以JSON格式返回的数据
            console.log(resData)
        }catch(err){
            console.log(err)
        }
    }
    document.querySelector("button").onclick = function(){
        getProvince();
    }
</script>

Request对象

除了使用基本的fetch方法,还可以通过创建一个Request对象来完成请求(实际上,每次请求,Fetch的内部都会帮你创建一个Request对象)

语法:new Request(url地址,响应配置对象)

例子:

    <button>得到所有的省份数据</button>
    <script>
        let req;
        function getRequestInfo(){
            if(!req){
                const url = 'http://101.132.72.36:5100/api/local';
                req = new Request(url,{})
                console.log(req);
            }
            return req.clone();//克隆一个全新的Request对象,配置一致
        }
    
        async function getProvinces(){
            try{
                const resData = await fetch(getRequestInfo());
                const result = await resData.json();
                console.log(resData);
                console.log(result);
            }catch(err){
                console.log(err)
            }
        }
        document.querySelector("button").onclick = function() {
            getProvinces();
        }
    </script>

注意点:尽量每次请求都是一个新的Requestd对象,因为如果请求需要发送大量数据的时候,比如post请求,数据需要放到请求体body中,如果发送的数据较多的时候,会形成一个流,流会记录上传的进度,如果重用该对象的话,会把上传的进度也给重用了,如果该Request对象数据量已经上传完了,进度是100%,如果再重用该request对象的话,会把进度也给重用了,这样会导致数据还没上传就已经结束了, 因此为了避免这种情况,Request也提供一种方法,可以调用Request构造器原型上clone()方法;该方法会克隆一个全新的Request对象,配置一致,但流传输是从最开始的位置开始的。

Response对象

response对象我们在上面已经提到过了,是调用fetch得到的promise在resolved的状态数据,不过,有时我们并不想去请求服务器,而是想模拟一个返回数据,这时,我们也可以利用Responsed构造器模拟出一个response对象返回

语法:new Response(响应体数据,配置对象(response对象里的属性))

例子

<button>得到模拟的response对象</button>
<script>
async function GetMockRes(){
    const res = new Response(`[
        {"id":1, "name":"北京"},
        {"id":2, "name":"天津"}
    ]`, {
        ok: true,
        status: 200
    })
    const resData = await res.json();
    console.log(resData);
}

document.querySelector("button").onclick = function() {
    GetMockRes();
}
</script>


Headers对象

无论是request还是response对象,内部都有一个headers对象,该request和response对象内部,会将传递的请求头信息,转换为Headers

语法:new Headers({请求头信息})

Headers对象中的方法:

  • has(key): 检查请求头中是否存在指定的key值
  • get(key): 得到请求头中对应的key值
  • set(key,value): 修改对应的键值对,如果没有对应的键,则新增
  • append(key,value): 添加对应的键值对,如果有对应的键,则把值加入
  • keys(): 得到所有的请求头键的集合
  • values(): 得到所有的请求头中值的集合
  • entries(): 得到所有请求头中键值对的集合

例子:

        const heads = new Headers({
            a:1,
            b:2,
            'Content-Type':'application/json',
            c:3
        })
        console.log(heads.has('a'));     //true
        console.log(heads.get('a'))      //1
        heads.set('a','11');             //a变成11
        heads.set('name','xiaohon');     //新增属性name,值为huang
        heads.append('sex','male')       //新增属性sex,值为male
        heads.append('sex','female')     //sex属性添加female值(male,female)
        heads.keys();       //得到的是一个iterator

        // function printKeys(heads){
        //     const keys = heads.keys();  //得到的是一个iterator
        //     for (const key of keys) {
        //         console.log(`key: ${key[0]}`)
        //     }

        // }
        // printKeys(heads)

        // function printValues(heads){
        //     const values = heads.values();
        //     for (const value of values) {
        //         console.log(`value: ${value[1]}`);
        //     }
        // }
        // printValues(heads)
        
        function printHeaders(heads) {
            const datas = heads.entries();
            for (const pair of datas) {
                console.log(`key: ${pair[0]},value: ${pair[1]}`);
            }
        }
        printHeaders(heads)

最后

用fetch api实现文件上传

流程:

  1. 客户端将文件数据发送给服务器 (我们需要做的只是这个)
  2. 服务器保存上传的文件数据到服务器端
  3. 服务器响应给客户的一个文件访问地址

测试地址:http://101.132.72.36:5100/api/upload 键的名称(表单域名称):imagefile

请求方法:post 请求的表单格式:multipart/form-data 请求体中必须包含一个键值对,键的名称是服务器要求的名称,值是文件数据

html5中,JS仍然无法随意的获取文件数据,但是可以获取到input元素中,被用户选中的文件数据 因此我们可以利用HTML5提供的formData构造函数来创建请求体

    <input type="file"  id="avatar">
    <img src="" alt="" id="imgAvatar">
    <button id="btn">上传</button>
    <script>
        async function upload(){
            const avatar = document.getElementById('avatar');
            if (avatar.files.length === 0) {
                alert('清选择要上传的图片');
                return;
            }
            console.log(avatar.files);
            //构建请求体
            const formData = new FormData;
            formData.append("imagefile", avatar.files[0]);  //请求体是键值对的形式
            const url = "http://101.132.72.36:5100/api/upload"
            const res = await fetch(url,{
                method: 'POST',
                body: formData
            })
            const result = await res.json();
            console.log(result);

            return result;
        }

        document.querySelector('button').onclick = async function(){
            const result = await upload();
            const img = document.getElementById("imgAvatar")
            img.src = result.path;
        }
    </script>