fetch的用法和示例详解

fetch是web提供的一个可以获取异步资源的api,目前还没有被所有浏览器支持,它提供的api返回的是Promise对象,所以你在了解这个api前首先得了解Promise的用法。

参考:阮老师的文章 ,另外也可查看我的转载:Promise 对象  (由于很多公司内网,对部分网站进行了过滤、封锁,导致文章或文章图片无法查看)

那我们首先讲讲在没有fetch的时候,我们是如何获取异步资源的:

//发送一个get请求是这样的:

//首先实例化一个XMLHttpRequest对象
var httpRequest = new XMLHttpRequest();

//注册httpRequest.readyState改变时会回调的函数,httpRequest.
//readyState共有5个可能的值,
//0	UNSENT (未打开)	open()方法还未被调用;
//1	OPENED  (未发送)	send()方法还未被调用;
//2	HEADERS_RECEIVED (已获取响应头)	send()方法已经被调用, 响应头和响应状态已经返回;
//3	LOADING (正在下载响应体)	响应体下载中; responseText中已经获取了部分数据;
//4	DONE (请求完成)	整个请求过程已经完毕.
httpRequest.onreadystatechange = function(){
 //该回调函数会被依次调用4次
 console.log(httpRequest.readyState);

 if(httpRequest.readyState===4){
   //请求已完成
   if(httpRequest.status===200){
     //http状态为200
     console.log(httpRequest.response);

     var data = JSON.parse(httpRequest.response);
     console.log(data);
   }
 }

}

//请求的网址
var url = "http://127.0.0.1:7777/list";
//该方法为初始化请求,第一个参数是请求的方法,比如GET,POST,PUT,第二个参数是请求的url
httpRequest.open('GET',url,true);

//设置http请求头
httpRequest.setRequestHeader("Content-Type","application/json");

//发出请求,参数为要发送的body体,如果是GET方法的话,一般无需发送body,设为空就可以
httpRequest.send(null);

关于XMLHttpRequest的更多用法请参照:https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest#open()

如果用了fetch之后,发送一个get请求是这样的:

//请求的网址
var url = "http://127.0.0.1:7777/list";
//发起get请求
var promise = fetch(url).then(function(response) {

   //response.status表示响应的http状态码
   if(response.status === 200){
     //json是返回的response提供的一个方法,会把返回的json字符串反序列化成对象,也被包装成一个Promise了
     return response.json();
   }else{
     return {}
   }

});
    
promise = promise.then(function(data){
  //响应的内容
	console.log(data);
}).catch(function(err){
	console.log(err);
})

接下来介绍下fetch的语法:

/**
参数:
input:定义要获取的资源。可能的值是:一个URL或者一个Request对象。
init:可选,是一个对象,参数有:
	method: 请求使用的方法,如 GET、POST。
	headers: 请求的头信息,形式为 Headers 对象或 ByteString。
	body: 请求的 body 信息:可能是一个 Blob、BufferSource、FormData、URLSearchParams 或者 USVString 对象。注意 GET  HEAD 方法的请求不能包含 body 信息。
	mode: 请求的模式,如 cors、 no-cors 或者 same-origin,默认为no-cors,该模式允许来自 CDN 的脚本、其他域的图片和其他一些跨域资源,但是首先有个前提条件,就是请求的 method 只能是HEAD、GET  POST。此外,如果 ServiceWorkers 拦截了这些请求,它不能随意添加或者修改除这些之外 Header 属性。第三,JS 不能访问 Response 对象中的任何属性,这确保了跨域时 ServiceWorkers 的安全和隐私信息泄漏问题。cors模式允许跨域请求,same-origin模式对于跨域的请求,将返回一个 error,这样确保所有的请求遵守同源策略。
	credentials: 请求的 credentials,如 omit、same-origin 或者 include。
	cache:  请求的 cache 模式: default, no-store, reload, no-cache, force-cache, or only-if-cached.
返回值:一个 Promise,resolve 时回传 Response 对象。
*/
fetch(input, init).then(function(response) {  });

一个发送post请求的示例:

fetch("http://127.0.0.1:7777/postContent", {
  method: "POST",
  headers: {
      "Content-Type": "application/json",
  },
  mode: "cors",
  body: JSON.stringify({
      content: "留言内容"
  })
}).then(function(res) {
  if (res.status === 200) {
      return res.json()
  } else {
      return Promise.reject(res.json())
  }
}).then(function(data) {
  console.log(data);
}).catch(function(err) {
  console.log(err);
});

如果考虑低版本浏览器的问题的话,引入https://github.com/github/fetch/blob/master/fetch.js 即可兼容。

 

出处:https://www.cnblogs.com/chris-oil/p/8430447.html

=======================================================================================

Fetch API 教程

fetch()是 XMLHttpRequest 的升级版,用于在 JavaScript 脚本里面发出 HTTP 请求。

浏览器原生提供这个对象。本文详细介绍它的用法。

一、基本用法

fetch()的功能与 XMLHttpRequest 基本相同,但有三个主要的差异。

(1)fetch()使用 Promise,不使用回调函数,因此大大简化了写法,写起来更简洁。

(2)fetch()采用模块化设计,API 分散在多个对象上(Response 对象、Request 对象、Headers 对象),更合理一些;相比之下,XMLHttpRequest 的 API 设计并不是很好,输入、输出、状态都在同一个接口管理,容易写出非常混乱的代码。

(3)fetch()通过数据流(Stream 对象)处理数据,可以分块读取,有利于提高网站性能表现,减少内存占用,对于请求大文件或者网速慢的场景相当有用。XMLHTTPRequest 对象不支持数据流,所有的数据必须放在缓存里,不支持分块读取,必须等待全部拿到后,再一次性吐出来。

在用法上,fetch()接受一个 URL 字符串作为参数,默认向该网址发出 GET 请求,返回一个 Promise 对象。它的基本用法如下。


fetch(url)
  .then(...)
  .catch(...)

下面是一个例子,从服务器获取 JSON 数据。


fetch('https://api.github.com/users/ruanyf')
  .then(response => response.json())
  .then(json => console.log(json))
  .catch(err => console.log('Request Failed', err)); 

上面示例中,fetch()接收到的response是一个 Stream 对象response.json()是一个异步操作,取出所有内容,并将其转为 JSON 对象。

Promise 可以使用 await 语法改写,使得语义更清晰。


async function getJSON() {
  let url = 'https://api.github.com/users/ruanyf';
  try {
    let response = await fetch(url);
    return await response.json();
  } catch (error) {
    console.log('Request Failed', error);
  }
}

上面示例中,await语句必须放在try...catch里面,这样才能捕捉异步操作中可能发生的错误。

后文都采用await的写法,不使用.then()的写法。

二、Response 对象:处理 HTTP 回应

2.1 Response 对象的同步属性

fetch()请求成功以后,得到的是一个 Response 对象。它对应服务器的 HTTP 回应。


const response = await fetch(url);

前面说过,Response 包含的数据通过 Stream 接口异步读取,但是它还包含一些同步属性,对应 HTTP 回应的标头信息(Headers),可以立即读取。


async function fetchText() {
  let response = await fetch('/readme.txt');
  console.log(response.status); 
  console.log(response.statusText);
}

上面示例中,response.statusresponse.statusText就是 Response 的同步属性,可以立即读取。

标头信息属性有下面这些。

Response.ok

Response.ok属性返回一个布尔值,表示请求是否成功,true对应 HTTP 请求的状态码 200 到 299,false对应其他的状态码。

Response.status

Response.status属性返回一个数字,表示 HTTP 回应的状态码(例如200,表示成功请求)。

Response.statusText

Response.statusText属性返回一个字符串,表示 HTTP 回应的状态信息(例如请求成功以后,服务器返回"OK")。

Response.url

Response.url属性返回请求的 URL。如果 URL 存在跳转,该属性返回的是最终 URL。

Response.type

Response.type属性返回请求的类型。可能的值如下:

  • basic:普通请求,即同源请求。
  • cors:跨域请求。
  • error:网络错误,主要用于 Service Worker。
  • opaque:如果fetch()请求的type属性设为no-cors,就会返回这个值,详见请求部分。表示发出的是简单的跨域请求,类似<form>表单的那种跨域请求。
  • opaqueredirect:如果fetch()请求的redirect属性设为manual,就会返回这个值,详见请求部分。

Response.redirected

Response.redirected属性返回一个布尔值,表示请求是否发生过跳转。

2.2 判断请求是否成功

fetch()发出请求以后,有一个很重要的注意点:只有网络错误,或者无法连接时,fetch()才会报错,其他情况都不会报错,而是认为请求成功。

这就是说,即使服务器返回的状态码是 4xx 或 5xx,fetch()也不会报错(即 Promise 不会变为 rejected状态)。

只有通过Response.status属性,得到 HTTP 回应的真实状态码,才能判断请求是否成功。请看下面的例子。


async function fetchText() {
  let response = await fetch('/readme.txt');
  if (response.status >= 200 && response.status < 300) {
    return await response.text();
  } else {
    throw new Error(response.statusText);
  }
}

上面示例中,response.status属性只有等于 2xx (200~299),才能认定请求成功。这里不用考虑网址跳转(状态码为 3xx),因为fetch()会将跳转的状态码自动转为 200。

另一种方法是判断response.ok是否为true


if (response.ok) {
  // 请求成功
} else {
  // 请求失败
}

2.3 Response.headers 属性

Response 对象还有一个Response.headers属性,指向一个 Headers 对象,对应 HTTP 回应的所有标头。

Headers 对象可以使用for...of循环进行遍历。


const response = await fetch(url);

for (let [key, value] of response.headers) { 
  console.log(`${key} : ${value}`);  
}

// 或者
for (let [key, value] of response.headers.entries()) { 
  console.log(`${key} : ${value}`);  
}

Headers 对象提供了以下方法,用来操作标头。

  • Headers.get():根据指定的键名,返回键值。
  • Headers.has(): 返回一个布尔值,表示是否包含某个标头。
  • Headers.set():将指定的键名设置为新的键值,如果该键名不存在则会添加。
  • Headers.append():添加标头。
  • Headers.delete():删除标头。
  • Headers.keys():返回一个遍历器,可以依次遍历所有键名。
  • Headers.values():返回一个遍历器,可以依次遍历所有键值。
  • Headers.entries():返回一个遍历器,可以依次遍历所有键值对([key, value])。
  • Headers.forEach():依次遍历标头,每个标头都会执行一次参数函数。

上面的有些方法可以修改标头,那是因为继承自 Headers 接口。对于 HTTP 回应来说,修改标头意义不大,况且很多标头是只读的,浏览器不允许修改。

这些方法中,最常用的是response.headers.get(),用于读取某个标头的值。


let response =  await  fetch(url);  
response.headers.get('Content-Type')
// application/json; charset=utf-8

Headers.keys()Headers.values()方法用来分别遍历标头的键名和键值。


// 键名
for(let key of myHeaders.keys()) {
  console.log(key);
}

// 键值
for(let value of myHeaders.values()) {
  console.log(value);
}

Headers.forEach()方法也可以遍历所有的键值和键名。


let response = await fetch(url);
response.headers.forEach(
  (value, key) => console.log(key, ':', value)
);

2.4 读取内容的方法

Response对象根据服务器返回的不同类型的数据,提供了不同的读取方法。

  • response.text():得到文本字符串。
  • response.json():得到 JSON 对象。
  • response.blob():得到二进制 Blob 对象。
  • response.formData():得到 FormData 表单对象。
  • response.arrayBuffer():得到二进制 ArrayBuffer 对象。

上面5个读取方法都是异步的,返回的都是 Promise 对象。必须等到异步操作结束,才能得到服务器返回的完整数据。

response.text()

response.text()可以用于获取文本数据,比如 HTML 文件。


const response = await fetch('/users.html');
const body = await response.text();
document.body.innerHTML = body

response.json()

response.json()主要用于获取服务器返回的 JSON 数据,前面已经举过例子了。

response.formData()

response.formData()主要用在 Service Worker 里面,拦截用户提交的表单,修改某些数据以后,再提交给服务器。

response.blob()

response.blob()用于获取二进制文件。


const response = await fetch('flower.jpg');
const myBlob = await response.blob();
const objectURL = URL.createObjectURL(myBlob);

const myImage = document.querySelector('img');
myImage.src = objectURL;

上面示例读取图片文件flower.jpg,显示在网页上。

response.arrayBuffer()

response.arrayBuffer()主要用于获取流媒体文件。


const audioCtx = new window.AudioContext();
const source = audioCtx.createBufferSource();

const response = await fetch('song.ogg');
const buffer = await response.arrayBuffer();

const decodeData = await audioCtx.decodeAudioData(buffer);
source.buffer = buffer;
source.connect(audioCtx.destination);
source.loop = true;

上面示例是response.arrayBuffer()获取音频文件song.ogg,然后在线播放的例子。

2.5 Response.clone()

Stream 对象只能读取一次,读取完就没了。这意味着,前一节的五个读取方法,只能使用一个,否则会报错。


let text =  await response.text();
let json =  await response.json();  // 报错

上面示例先使用了response.text(),就把 Stream 读完了。后面再调用response.json(),就没有内容可读了,所以报错。

Response 对象提供Response.clone()方法,创建Response对象的副本,实现多次读取。


const response1 = await fetch('flowers.jpg');
const response2 = response1.clone();

const myBlob1 = await response1.blob();
const myBlob2 = await response2.blob();

image1.src = URL.createObjectURL(myBlob1);
image2.src = URL.createObjectURL(myBlob2);

上面示例中,response.clone()复制了一份 Response 对象,然后将同一张图片读取了两次。

Response 对象还有一个Response.redirect()方法,用于将 Response 结果重定向到指定的 URL。该方法一般只用在 Service Worker 里面,这里就不介绍了。

2.6 Response.body 属性

Response.body属性是 Response 对象暴露出的底层接口,返回一个 ReadableStream 对象,供用户操作。

它可以用来分块读取内容,应用之一就是显示下载的进度。


const response = await fetch('flower.jpg');
const reader = response.body.getReader();

while(true) {
  const {done, value} = await reader.read();

  if (done) {
    break;
  }

  console.log(`Received ${value.length} bytes`)
}

上面示例中,response.body.getReader()方法返回一个遍历器。这个遍历器的read()方法每次返回一个对象,表示本次读取的内容块。

这个对象的done属性是一个布尔值,用来判断有没有读完;value属性是一个 arrayBuffer 数组,表示内容块的内容,而value.length属性是当前块的大小。

三、fetch()的第二个参数:定制 HTTP 请求

fetch()的第一个参数是 URL,还可以接受第二个参数,作为配置对象,定制发出的 HTTP 请求。


fetch(url, optionObj)

上面命令的optionObj就是第二个参数。

HTTP 请求的方法、标头、数据体都在这个对象里面设置。下面是一些示例。

(1)POST 请求


const response = await fetch(url, {
  method: 'POST',
  headers: {
    "Content-type": "application/x-www-form-urlencoded; charset=UTF-8",
  },
  body: 'foo=bar&lorem=ipsum',
});

const json = await response.json();

上面示例中,配置对象用到了三个属性。

  • method:HTTP 请求的方法,POSTDELETEPUT都在这个属性设置。
  • headers:一个对象,用来定制 HTTP 请求的标头。
  • body:POST 请求的数据体。

注意,有些标头不能通过headers属性设置,比如Content-LengthCookieHost等等。它们是由浏览器自动生成,无法修改。

(2)提交 JSON 数据


const user =  { name:  'John', surname:  'Smith'  };
const response = await fetch('/article/fetch/post/user', {
  method: 'POST',
  headers: {
   'Content-Type': 'application/json;charset=utf-8'
  }, 
  body: JSON.stringify(user) 
});

上面示例中,标头Content-Type要设成'application/json;charset=utf-8'。因为默认发送的是纯文本,Content-Type的默认值是'text/plain;charset=UTF-8'

(3)提交表单


const form = document.querySelector('form');

const response = await fetch('/users', {
  method: 'POST',
  body: new FormData(form)
})

(4)文件上传

如果表单里面有文件选择器,可以用前一个例子的写法,上传的文件包含在整个表单里面,一起提交。

另一种方法是用脚本添加文件,构造出一个表单,进行上传,请看下面的例子。


const input = document.querySelector('input[type="file"]');

const data = new FormData();
data.append('file', input.files[0]);
data.append('user', 'foo');

fetch('/avatars', {
  method: 'POST',
  body: data
});

上传二进制文件时,不用修改标头的Content-Type,浏览器会自动设置。

(5)直接上传二进制数据

fetch()也可以直接上传二进制数据,将 Blob 或 arrayBuffer 数据放在body属性里面。


let blob = await new Promise(resolve =>   
  canvasElem.toBlob(resolve,  'image/png')
);

let response = await fetch('/article/fetch/post/image', {
  method:  'POST',
  body: blob
});

四、fetch()配置对象的完整 API

fetch()第二个参数的完整 API 如下。


const response = fetch(url, {
  method: "GET",
  headers: {
    "Content-Type": "text/plain;charset=UTF-8"
  },
  body: undefined,
  referrer: "about:client",
  referrerPolicy: "no-referrer-when-downgrade",
  mode: "cors", 
  credentials: "same-origin",
  cache: "default",
  redirect: "follow",
  integrity: "",
  keepalive: false,
  signal: undefined
});

fetch()请求的底层用的是 Request() 对象的接口,参数完全一样,因此上面的 API 也是Request()的 API。

这些属性里面,headersbodymethod前面已经给过示例了,下面是其他属性的介绍。

cache

cache属性指定如何处理缓存。可能的取值如下:

  • default:默认值,先在缓存里面寻找匹配的请求。
  • no-store:直接请求远程服务器,并且不更新缓存。
  • reload:直接请求远程服务器,并且更新缓存。
  • no-cache:将服务器资源跟本地缓存进行比较,有新的版本才使用服务器资源,否则使用缓存。
  • force-cache:缓存优先,只有不存在缓存的情况下,才请求远程服务器。
  • only-if-cached:只检查缓存,如果缓存里面不存在,将返回504错误。

mode

mode属性指定请求的模式。可能的取值如下:

  • cors:默认值,允许跨域请求。
  • same-origin:只允许同源请求。
  • no-cors:请求方法只限于 GET、POST 和 HEAD,并且只能使用有限的几个简单标头,不能添加跨域的复杂标头,相当于提交表单所能发出的请求。

credentials

credentials属性指定是否发送 Cookie。可能的取值如下:

  • same-origin:默认值,同源请求时发送 Cookie,跨域请求时不发送。
  • include:不管同源请求,还是跨域请求,一律发送 Cookie。
  • omit:一律不发送。

跨域请求发送 Cookie,需要将credentials属性设为include


fetch('http://another.com', {
  credentials: "include"
});

signal

signal属性指定一个 AbortSignal 实例,用于取消fetch()请求,详见下一节。

keepalive

keepalive属性用于页面卸载时,告诉浏览器在后台保持连接,继续发送数据。

一个典型的场景就是,用户离开网页时,脚本向服务器提交一些用户行为的统计信息。这时,如果不用keepalive属性,数据可能无法发送,因为浏览器已经把页面卸载了。


window.onunload = function() {
  fetch('/analytics', {
    method: 'POST',
    body: "statistics",
    keepalive: true
  });
};

redirect

redirect属性指定 HTTP 跳转的处理方法。可能的取值如下:

  • follow:默认值,fetch()跟随 HTTP 跳转。
  • error:如果发生跳转,fetch()就报错。
  • manualfetch()不跟随 HTTP 跳转,但是response.url属性会指向新的 URL,response.redirected属性会变为true,由开发者自己决定后续如何处理跳转。

integrity

integrity属性指定一个哈希值,用于检查 HTTP 回应传回的数据是否等于这个预先设定的哈希值。

比如,下载文件时,检查文件的 SHA-256 哈希值是否相符,确保没有被篡改。


fetch('http://site.com/file', {
  integrity: 'sha256-abcdef'
});

referrer

referrer属性用于设定fetch()请求的referer标头。

这个属性可以为任意字符串,也可以设为空字符串(即不发送referer标头)。


fetch('/page', {
  referrer: ''
});

referrerPolicy

referrerPolicy属性用于设定Referer标头的规则。可能的取值如下:

  • no-referrer-when-downgrade:默认值,总是发送Referer标头,除非从 HTTPS 页面请求 HTTP 资源时不发送。
  • no-referrer:不发送Referer标头。
  • originReferer标头只包含域名,不包含完整的路径。
  • origin-when-cross-origin:同源请求Referer标头包含完整的路径,跨域请求只包含域名。
  • same-origin:跨域请求不发送Referer,同源请求发送。
  • strict-originReferer标头只包含域名,HTTPS 页面请求 HTTP 资源时不发送Referer标头。
  • strict-origin-when-cross-origin:同源请求时Referer标头包含完整路径,跨域请求时只包含域名,HTTPS 页面请求 HTTP 资源时不发送该标头。
  • unsafe-url:不管什么情况,总是发送Referer标头。

五、取消fetch()请求

fetch()请求发送以后,如果中途想要取消,需要使用AbortController对象。


let controller = new AbortController();
let signal = controller.signal;

fetch(url, {
  signal: controller.signal
});

signal.addEventListener('abort',
  () => console.log('abort!')
);

controller.abort(); // 取消

console.log(signal.aborted); // true

上面示例中,首先新建 AbortController 实例,然后发送fetch()请求,配置对象的signal属性必须指定接收 AbortController 实例发送的信号controller.signal

controller.abort()方法用于发出取消信号。这时会触发abort事件,这个事件可以监听,也可以通过controller.signal.aborted属性判断取消信号是否已经发出。

下面是一个1秒后自动取消请求的例子。


let controller = new AbortController();
setTimeout(() => controller.abort(), 1000);

try {
  let response = await fetch('/long-operation', {
    signal: controller.signal
  });
} catch(err) {
  if (err.name == 'AbortError') {
    console.log('Aborted!');
  } else {
    throw err;
  }
}

六、参考链接

(完)

 

出处:https://www.ruanyifeng.com/blog/2020/12/fetch-tutorial.html

=======================================================================================

使用 Fetch

Fetch API 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 fetch() 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。

这种功能以前是使用 XMLHttpRequest 实现的。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如  Service Workers。Fetch 还提供了专门的逻辑空间来定义其他与 HTTP 相关的概念,例如 CORS 和 HTTP 的扩展。

请注意,fetch 规范与 jQuery.ajax() 主要有三种方式的不同:

  • 当接收到一个代表错误的 HTTP 状态码时,从 fetch() 返回的 Promise 不会被标记为 reject, 即使响应的 HTTP 状态码是 404 或 500。相反,它会将 Promise 状态标记为 resolve (但是会将 resolve 的返回值的 ok 属性设置为 false ),仅当网络故障时或请求被阻止时,才会标记为 reject。
  • fetch() 可以不会接受跨域 cookies;你也可以不能使用 fetch() 建立起跨域会话。其他网站的 Set-Cookie 头部字段将会被无视。
  • fetch 不会发送 cookies。除非你使用了credentials初始化选项。(自 2017 年 8 月 25 日以后,默认的 credentials 政策变更为 same-origin。Firefox 也在 61.0b13 版本中进行了修改)

一个基本的 fetch 请求设置起来很简单。看看下面的代码:

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

这里我们通过网络获取一个 JSON 文件并将其打印到控制台。最简单的用法是只提供一个参数用来指明想 fetch() 到的资源路径,然后返回一个包含响应结果的promise(一个 Response 对象)。

当然它只是一个 HTTP 响应,而不是真的JSON。为了获取JSON的内容,我们需要使用 json() 方法(在 Body mixin 中定义,被 RequestResponse 对象实现)。

注意:Body mixin 还有其他相似的方法,用于获取其他类型的内容。参考 Body

最好使用符合内容安全策略 (CSP)的链接而不是使用直接指向资源地址的方式来进行Fetch的请求。

支持的请求参数

fetch() 接受第二个可选参数,一个可以控制不同配置的 init 对象:

参考 fetch(),查看所有可选的配置和更多描述。

// Example POST method implementation:

postData('http://example.com/answer', {answer: 42})
  .then(data => console.log(data)) // JSON from `response.json()` call
  .catch(error => console.error(error))

function postData(url, data) {
  // Default options are marked with *
  return fetch(url, {
    body: JSON.stringify(data), // must match 'Content-Type' header
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin', // include, same-origin, *omit
    headers: {
      'user-agent': 'Mozilla/4.0 MDN Example',
      'content-type': 'application/json'
    },
    method: 'POST', // *GET, POST, PUT, DELETE, etc.
    mode: 'cors', // no-cors, cors, *same-origin
    redirect: 'follow', // manual, *follow, error
    referrer: 'no-referrer', // *client, no-referrer
  })
  .then(response => response.json()) // parses response to JSON
}

发送带凭据的请求

为了让浏览器发送包含凭据的请求(即使是跨域源),要将credentials: 'include'添加到传递给 fetch()方法的init对象。

fetch('https://example.com', {
  credentials: 'include'
})

如果你只想在请求URL与调用脚本位于同一起源处时发送凭据,请添加 credentials: 'same-origin'

// The calling script is on the origin 'https://example.com'

fetch('https://example.com', {
  credentials: 'same-origin'
})

要改为确保浏览器不在请求中包含凭据,请使用 credentials: 'omit'

fetch('https://example.com', {
  credentials: 'omit'
})

上传 JSON 数据

使用 fetch() POST JSON数据

var url = 'https://example.com/profile';
var data = {username: 'example'};

fetch(url, {
  method: 'POST', // or 'PUT'
  body: JSON.stringify(data), // data can be `string` or {object}!
  headers: new Headers({
    'Content-Type': 'application/json'
  })
}).then(res => res.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

上传文件

可以通过 HTML <input type="file" /> 元素,FormData()fetch() 上传文件。

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

上传多个文件

可以通过HTML <input type="file" mutiple/> 元素,FormData()fetch() 上传文件。

var formData = new FormData();
var photos = document.querySelector("input[type='file'][multiple]");

formData.append('title', 'My Vegas Vacation');
// formData 只接受文件、Blob 或字符串,不能直接传递数组,所以必须循环嵌入
for (let i = 0; i < photos.files.length; i++) {
    formData.append('photo', photos.files[i]);
}

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData
})
.then(response => response.json())
.then(response => console.log('Success:', JSON.stringify(response)))
.catch(error => console.error('Error:', error));

检测请求是否成功

如果遇到网络故障,fetch() promise 将会 reject,带上一个 TypeError 对象。虽然这个情况经常是遇到了权限问题或类似问题——比如 404 不是一个网络故障。想要精确的判断 fetch() 是否成功,需要包含 promise resolved 的情况,此时再判断 Response.ok 是不是为 true。类似以下代码:

fetch('flowers.jpg').then(function(response) {
  if(response.ok) {
    return response.blob();
  }
  throw new Error('Network response was not ok.');
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
}).catch(function(error) {
  console.log('There has been a problem with your fetch operation: ', error.message);
});

自定义请求对象

除了传给 fetch() 一个资源的地址,你还可以通过使用 Request() 构造函数来创建一个 request 对象,然后再作为参数传给 fetch()

var myHeaders = new Headers();

var myInit = { method: 'GET',
               headers: myHeaders,
               mode: 'cors',
               cache: 'default' };

var myRequest = new Request('flowers.jpg', myInit);

fetch(myRequest).then(function(response) {
  return response.blob();
}).then(function(myBlob) {
  var objectURL = URL.createObjectURL(myBlob);
  myImage.src = objectURL;
});

Request()fetch() 接受同样的参数。你甚至可以传入一个已存在的 request 对象来创造一个拷贝:

var anotherRequest = new Request(myRequest,myInit);

这个很有用,因为 request 和 response bodies 只能被使用一次(译者注:这里的意思是因为设计成了 stream 的方式,所以它们只能被读取一次)。创建一个拷贝就可以再次使用 request/response 了,当然也可以使用不同的 init 参数。

注意clone() 方法也可以用于创建一个拷贝。它和上述方法一样,如果 request 或 response 的 body 已经被读取过,那么将执行失败。区别在于, clone() 出的 body 被读取不会导致原 body 被标记为已读取。

Headers

使用 Headers 的接口,你可以通过 Headers() 构造函数来创建一个你自己的 headers 对象。一个 headers 对象是一个简单的多名值对:

var content = "Hello World";
var myHeaders = new Headers();
myHeaders.append("Content-Type", "text/plain");
myHeaders.append("Content-Length", content.length.toString());
myHeaders.append("X-Custom-Header", "ProcessThisImmediately");

也可以传一个多维数组或者对象字面量:

myHeaders = new Headers({
  "Content-Type": "text/plain",
  "Content-Length": content.length.toString(),
  "X-Custom-Header": "ProcessThisImmediately",
});

它的内容可以被获取:

console.log(myHeaders.has("Content-Type")); // true
console.log(myHeaders.has("Set-Cookie")); // false
myHeaders.set("Content-Type", "text/html");
myHeaders.append("X-Custom-Header", "AnotherValue");

console.log(myHeaders.get("Content-Length")); // 11
console.log(myHeaders.getAll("X-Custom-Header")); // ["ProcessThisImmediately", "AnotherValue"]

myHeaders.delete("X-Custom-Header");
console.log(myHeaders.getAll("X-Custom-Header")); // [ ]

虽然一些操作只能在 ServiceWorkers 中使用,但是它提供了更方便的操作 Headers 的 API。

如果使用了一个不合法的HTTP Header属性名,那么Headers的方法通常都抛出 TypeError 异常。如果不小心写入了一个不可写的属性,也会抛出一个 TypeError 异常。除此以外的情况,失败了并不抛出异常。例如:

var myResponse = Response.error();
try {
  myResponse.headers.set("Origin", "http://mybank.com");
} catch(e) {
  console.log("Cannot pretend to be a bank!");
}

最好在在使用之前检查内容类型 content-type 是否正确,比如:

fetch(myRequest).then(function(response) {
  if(response.headers.get("content-type") === "application/json") {
    return response.json().then(function(json) {
      // process your JSON further
    });
  } else {
    console.log("Oops, we haven't got JSON!");
  }
});

Guard

由于 Headers 可以在 request 请求中被发送或者在 response 请求中被接收,并且规定了哪些参数是可写的,Headers 对象有一个特殊的 guard 属性。这个属性没有暴露给 Web,但是它影响到哪些内容可以在 Headers 对象中被操作。

可能的值如下:

  • none:默认的
  • request:从 request 中获得的 headers(Request.headers)只读
  • request-no-cors:从不同域(Request.mode no-cors)的 request 中获得的 headers 只读
  • response:从 response 中获得的 headers(Response.headers)只读
  • immutable:在 ServiceWorkers 中最常用的,所有的 headers 都只读。

注意:你不可以添加或者修改一个 guard 属性是 request 的 Request Header 的 Content-Length 属性。同样地,插入 Set-Cookie 属性到一个 response header 是不允许的,因此,Service Worker 中,不能给合成的 Response 的 header 添加一些 cookie。

Response 对象

如上所述,Response 实例是在 fetch() 处理完 promise 之后返回的。

你会用到的最常见的 response 属性有:

  • Response.status — 整数(默认值为200)为response的状态码。
  • Response.statusText — 字符串(默认值为"OK"),该值与 HTTP 状态码消息对应。
  • Response.ok — 如上所示,该属性是来检查response的状态是否在 200 - 299(包括200 和 299)这个范围内。该属性返回一个布尔值

它的实例也可用通过 JavaScript 来创建,但只有在 ServiceWorkers 中才真正有用,当使用 respondWith() 方法并提供了一个自定义的 response 来接受 request 时:

var myBody = new Blob();

addEventListener('fetch', function(event) {
  event.respondWith(new Response(myBody, {
    headers: { "Content-Type" : "text/plain" }
  });
});

Response() 构造方法接受两个可选参数—— response 的数据体和一个初始化对象(与Request() 所接受的 init 参数类似。)

注意: 静态方法 error() 只是返回了错误的response。与此类似地,redirect() 只是返回了一个可以重定向至某 URL 的 response。这些也只与 Service Worker 有关。

Body

不管是请求还是响应都能够包含 body 对象。body 也可以是以下任意类型的实例。

Body 类定义了以下方法(这些方法都被 RequestResponse所实现)以获取 body 内容。这些方法都会返回一个被解析后的Promise对象和数据。

比起XHR来,这些方法让非文本化的数据使用起来更加简单。

请求体可以由传入 body 参数来进行设置:

var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
  method: "POST",
  body: form
})

request和response(包括 fetch() 方法)都会试着自动设置 Content-Type。如果没有设置 Content-Type 值,发送的请求也会自动设值。

特性检测

Fetch API 的支持情况,可以通过检测Headers, Request, Responsefetch()是否在WindowWorker 域中。例如:

if(self.fetch) {
    // run my fetch request here
} else {
    // do something with XMLHttpRequest?
}

Polyfill

如果要在不支持的浏览器中使用 Fetch,可以使用 Fetch Polyfill

规范

详细说明状态注释
Fetch Living Standard Initial definition

浏览器兼容性

Report problems with this compatibility data on GitHub

  desktopmobile
  ChromeEdgeFirefoxInternet ExplorerOperaSafariWebView AndroidChrome AndroidFirefox AndroidOpera AndroidiOS SafariSamsung Internet
fetch
Experimental
ChromeFull support42
 
EdgeFull support14
 
FirefoxFull support39
 
Internet ExplorerNo supportNo
 
OperaFull support29
 
SafariFull support10.1
 
WebView AndroidFull support42
 
Chrome AndroidFull support42
 
Firefox AndroidFull support39
 
Opera AndroidFull support29
 
iOS SafariFull support10.3
 
Samsung InternetFull support4.0
 
Support for blob: and data:
Experimental
ChromeFull support48
 
EdgeFull support79
 
FirefoxCompatibility unknown; please update this.?
 
Internet ExplorerNo supportNo
 
OperaCompatibility unknown; please update this.?
 
SafariCompatibility unknown; please update this.?
 
WebView AndroidFull support43
 
Chrome AndroidFull support48
 
Firefox AndroidCompatibility unknown; please update this.?
 
Opera AndroidCompatibility unknown; please update this.?
 
iOS SafariCompatibility unknown; please update this.?
 
Samsung InternetFull support5.0
 
referrerPolicy
ChromeFull support52
 
EdgeFull support79
 
FirefoxFull support52
 
Internet ExplorerNo supportNo
 
OperaFull support39
 
SafariFull support11.1
 
WebView AndroidFull support52
 
Chrome AndroidFull support52
 
Firefox AndroidFull support52
 
Opera AndroidFull support41
 
iOS SafariNo supportNo
 
Samsung InternetFull support6.0
 
signal
Experimental
ChromeFull support66
 
EdgeFull support16
 
FirefoxFull support57
 
Internet ExplorerNo supportNo
 
OperaFull support53
 
SafariFull support11.1
 
WebView AndroidFull support66
 
Chrome AndroidFull support66
 
Firefox AndroidFull support57
 
Opera AndroidFull support47
 
iOS SafariFull support11.3
 
Samsung InternetFull support9.0
 
Streaming response body
Experimental
ChromeFull support43
 
EdgeFull support14
 
FirefoxFull supportYes
disabled
Internet ExplorerNo supportNo
 
OperaFull support29
 
SafariFull support10.1
 
WebView AndroidFull support43
 
Chrome AndroidFull support43
 
Firefox AndroidNo supportNo
 
Opera AndroidNo supportNo
 
iOS SafariFull support10.3
 
Samsung InternetFull support4.0
 

Legend

Full supportFull support
No supportNo support
Compatibility unknownCompatibility unknown
Experimental. Expect behavior to change in the future.
See implementation notes.
User must explicitly enable this feature.

参见

 

出处:https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch

=======================================================================================

Fetch使用方法

前言:

  fetch是用来取代传统的XMLHttpRequest的。 它的优点很多,包括链式调用的语法、返回promise等。

 

什么是fetch?

  fetch api是基于promise的设计,它是为了取代传统xhr的不合理的写法而生的。

 

WHY fetch?

  xhr请求写起来非常的混乱,如下所示:

复制代码
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';

xhr.onload = function() {
  console.log(xhr.response);
};

xhr.onerror = function() {
  console.log("Oops, error");
};

xhr.send();
复制代码

  

  但是使用fetch之后,如下所示:

复制代码
fetch(url).then(function(response) {
  return response.json();
}).then(function(data) {
  console.log(data);
}).catch(function(e) {
  console.log("Oops, error");
});
复制代码

  这种链式调用的风格看上去会非常舒服。

 

  如果我们再使用了箭头函数就会更加简洁了。 

fetch(url).then(response => response.json())
  .then(data => console.log(data))
  .catch(e => console.log("Oops, error", e))

  

  通过使用fetch api,可以将传统的xhr的粗糙的使用方法转化的如此精简,实在是好! 

 

  但是呢? 使用Promise,还是可以很明显看到callback的使用,不急,我们还可以使用 async、await :

复制代码
// Async/Await requirements: Latest Chrome/FF browser or Babel: https://babeljs.io/docs/plugins/transform-async-to-generator/
// Fetch requirements: Latest Chrome/FF browser or Github fetch polyfill: https://github.com/github/fetch

// async function
async function fetchAsync () {
  // await response of fetch call
  let response = await fetch('https://api.github.com');
  // only proceed once promise is resolved
  let data = await response.json();
  // only proceed once second promise is resolved
  return data;
}

// trigger async function
// log response or catch error of fetch promise
fetchAsync()
    .then(data => console.log(data))
    .catch(reason => console.log(reason.message))
复制代码

  这样看上去是不是就好多了呢?

注意: 对于async和await的使用还是很明确的,就是一般我们在一个异步函数之前添加 await 关键词,然后在这个 await 的相关代码外面使用的时 async 函数,这样的结合才是合适的。

利用 async 和 await,我们就可以像写同步代码一样来写异步代码啦! 

但是呢,目前async 和 await 的支持性还不是很好,目前还无法在一般的浏览器中使用!

   

 

 

 

 

 

 

 

 

 

  

基本使用方法:

fetch必须接受一个资源路径作为参数,并且返回了一个promise,所以我们可以直接使用链式调用的方式。 

复制代码
 fetch("/getAllProduct").then(function(res) {
            return res.json();
        }).then(function (data) {
            if (data.code == 200) {
              console.log('获取到所有产品' ,data.data);
              that.props.addAllProduct(data.data);
            } else {
              console.log(data.message);
            }
        })
复制代码

这样,我们就可以发送一个ajax请求。

复制代码
/* 对客户端的返回数据封装
 * @param [code] (number) code为返回的状态码
 * @param [message] (string) message为返回的信息
 * @param [data] (any) data是可选的,为返回给前端的数据
 */
// 注意: retrunJson中的res为node处理接口的回调函数中的res,这个是必须的。
function returnJson(res, code, message, data) {
    var response = {
        code: code,
        message: message
    };
    if (typeof data !== 'undefined') {
        response.data = data;
    }
    res.json(response);
   // 返回这个请求之后,必须要 res.end()表示请求的结束,否则后台可能会崩溃。 res.end(); } router.post(
'/register', function (req, res) { let userName = req.body.username, password = req.body.password, passwordAgain = req.body.passwordAgain, type = req.body.type; console.log(userName, password, type); if (type == 1) { if (password == passwordAgain) { let managerId = uuidv1(); console.log(userName, password, passwordAgain); var newUser = new Manager({ name: userName, password: password, type: req.body.type, managerId: managerId }); Manager.find(userName, function (err, user) { if (err) { returnJson(res, 5001, '服务器错误,注册失败'); } else { if (user !== null) { returnJson(res, 4003, "此用户已经注册!"); } else { // 如果符合条件,就注册该用户,将数据保存在数据库。 newUser.save(function (err, user) { if (err) { // 服务器端错误,失败返回状态码500 returnJson(res, 500, "用户注册失败!"); } else { // user数据较简单,直接传递user即可,如果复杂,我们可以考虑使用对象形式传递更多数据。 returnJson(res, 200, "用户注册成功!", user); } }); } } }); } else { returnJson(res, 4001, "用户两次输入密码不一致!"); } } else if( type == 2) { if (password == passwordAgain) { let userId = uuidv1(); console.log(userName, password, passwordAgain); var newUser = new User({ name: userName, password: password, type: req.body.type, userId: userId }); User.find(userName, function (err, user) { if (err) { returnJson(res, 5001, '服务器错误,注册失败'); } else { if (user !== null) { returnJson(res, 4003, "此用户已经注册!"); } else { // 如果符合条件,就注册该用户,将数据保存在数据库。 newUser.save(function (err, user) { if (err) { // 服务器端错误,失败返回状态码500 returnJson(res, 500, "用户注册失败!"); } else { // user数据较简单,直接传递user即可,如果复杂,我们可以考虑使用对象形式传递更多数据。 returnJson(res, 200, "用户注册成功!", user); } }); } } }); } else { returnJson(res, 4001, "用户两次输入密码不一致!"); } } });
复制代码

这样,我们就可以处理一个ajax请求。

 

注意点:

1、fetch() 返回的是一个Promise对象。

  fetch使用的promise对象可以使得我们使用同步的方式写异步函数。

 

2、 fetch api是可以结合 async 和 await 来使用的。 

  fetch是基于promise实现的,但是使用promise的写法,我们还是可以看到callback的影子,如果结合 async和await来使用,还是非常不错的。

 

3、 Fetch api 提供的spi囊括但是不限于xhr的所有功能。

 

4、 fetch api 可以跨域。 

  参考: https://fetch.spec.whatwg.org/#http-cors-protocol

  跨域请求必须包括  Origin 作为header. 

     以上。

    我们在发送fetch请求的时候就会使用到CORS协议,尽管这些对于开发者来说是透明的,但是浏览器还是会发送 origin 字段。 

              那么,这里推荐自己用 fecth 吗?实际上,因为我不太信任它,原因有很多。因此我在用一个叫 axios 的库,也是基于 promise 的,同时非常可靠。

 

5、fetch提供了对request和response对象的通用定义。

  Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义。所以在一个Fetch请求中,完全可以只使用Request 和 Response两个对象,通过Request 设置参数,通过Response 对返回值进行处理。 

 所以,我们可以将一个fetch定义如下:

复制代码
var myHeaders = new Headers();
myHeaders.append('Content-Type', 'image/jpeg');
var option = { method: 'GET',
    headers: myHeaders,
    mode: 'cors',
    cache: 'default' };
var myRequest = new Request('https://api.github.com/users/mzabriskie',option);
fetch(myRequest).then(function(response) {
    ... 
});
复制代码

   

参考文章: https://github.com/camsong/blog/issues/2

 

出处:https://www.cnblogs.com/zhuzhenwei918/p/7249691.html

posted on 2021-02-04 15:46  jack_Meng  阅读(19732)  评论(0)    收藏  举报

导航