fetch使用的常见问题及其解决办法

2020-01-26 作者:网站首页   |   浏览(149)

时间: 2019-06-02阅读: 393标签: fetch

简介

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

首先声明一下,本文不是要讲解fetch的具体用法,不清楚的可以参考MDN fetch教程。

与ajax的区别

1.从 fetch()返回的 Promise 将不会拒绝HTTP错误状态, 即使响应是一个 HTTP 404 或 500。相反,它会正常解决 (其中ok状态设置为false), 并且仅在网络故障时或任何阻止请求完成时,它才会拒绝。
2.默认情况下, fetch在服务端不会发送或接收任何 cookies, 如果站点依赖于维护一个用户会话,则导致未经认证的请求(要发送 cookies,必须发送凭据头)。
说明:使用fetch模式,request header头是不会带cookies的,这样服务端就认为是不同的会话,解决方案:
设置fetch的options就好,我的如下:
const options= { method: "GET",mode: 'cors',credentials: 'include'};//same-origin
我因为用cors,所以credentials设置为include,如果不跨域,那么same-origin就行了。

fetch默认不携带cookie

特征检查

可以通过检查 Headers、Request、Response 或 fetch 在 window 或 worker 作用域中是否存在,来检查是否支持 Fetch API。

配置其 credentials 项,其有3个值:

简单示例

fetch API中最常用的是fetch方法,该方法最简单的形式是,接受一个 URL 参数并返回以一个 promise 对象:

fetch("/data.json").then(function(res) {
  // res instanceof Response == true.
  if (res.ok) {
    res.json().then(function(data) {
      console.log(data.entries);
    });
  } else {
    console.log("Looks like the response wasn't perfect, got status", res.status);
  }
}, function(e) {
  console.log("Fetch failed!", e);
});

如果提交的是一个post请求

fetch("http://www.example.org/submit.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "firstName=Nikhil&favColor=blue&password=easytoguess"
}).then(function(res) {
  if (res.ok) {
    alert("Perfect! Your settings are saved.");
  } else if (res.status == 401) {
    alert("Oops! You are not authorized.");
  }
}, function(e) {
  alert("Error submitting form!");
});

omit: 默认值,忽略cookie的发送same-origin: 表示cookie只能同域发送,不能跨域发送include: cookie既可以同域发送,也可以跨域发送

yzc216亚洲城,引入的接口

Fetch 引入了 3 个接口,分别是 Headers,Request 和 Response。

credentials所表达的含义,其实与XHR2中的withCredentials属性类似,表示请求是否携带cookie

Headers

Headers 接口是一个简单的键值对:

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

也可以给构造函数传一个多维数组或 JS 字面量对象:

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

结合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;
});

这样,若要fetch请求携带cookie信息,只需设置一下credentials选项即可,例如fetch(url, {credentials: 'include'});

Request

通过构造一个 Request 对象来获取网络资源,构造函数需要 URL、method 和 headers 参数,同时也可以提供请求体(body)、请求模式(mode)、credentials 和 cache hints 等参数。

var uploadReq = new Request("/uploadImage", {
  method: "POST",
  headers: {
    "Content-Type": "image/png",
  },
  body: "image data"
});

mode 参数用来决定是否允许跨域请求,以及哪些 response 属性可读。可选的 mode 值为 "same-origin"、"no-cors"(默认)以及 "cors"。

fetch请求对某些错误http状态不会reject

same-origin

该模式很简单,如果一个请求是跨域的,那么将返回一个 error,这样确保所有的请求遵守同源策略。

这主要是由fetch返回promise导致的,因为fetch返回的promise在某些错误的http状态下如400、500等不会reject,相反它会被resolve;只有网络错误会导致请求不能完成时,fetch 才会被 reject;所以一般会对fetch请求做一层封装,例如下面代码所示:

no-cors

该模式允许来自 CDN 的脚本、其他域的图片和其他一些跨域资源,但是首先有个前提条件,就是请求的 method 只能是HEAD
、GET 或 POST。此外,如果 ServiceWorkers 拦截了这些请求,它不能随意添加或者修改除这些之外 Header 属性。第三,JS 不能访问 Response 对象中的任何属性,这确保了跨域时 ServiceWorkers 的安全和隐私信息泄漏问题。

function checkStatus(response) { if (response.status = 200  response.status  300) { return response; } const error = new Error(response.statusText); error.response = response; throw error;}function parseJSON(response) { return response.json();}export default function request(url, options) { let opt = options||{}; return fetch(url, {credentials: 'include', ...opt}) .then(checkStatus) .then(parseJSON) .then((data) = ( data )) .catch((err) = ( err ));}
cors

该模式通常用于跨域请求,用来从第三方提供的 API 获取数据。该模式遵守 CORS 协议,并只有有限的一些 Header 被暴露给 Response 对象,但是 body 是可读的。例如,获取一个 Flickr 最感兴趣的照片的清单:

var u = new URLSearchParams();
u.append('method', 'flickr.interestingness.getList');
u.append('api_key', '<insert api key here>');
u.append('format', 'json');
u.append('nojsoncallback', '1');

var apiCall = fetch('https://api.flickr.com/services/rest?'   u);

apiCall.then(function(response) {
  return response.json().then(function(json) {
    // photo is a list of photos.
    return json.photos.photo;
  });
}).then(function(photos) {
  photos.forEach(function(photo) {
    console.log(photo.title);
  });
});

另外,credentials 属性决定了是否可以跨域访问 cookie 。该属性与 XHR 的
withCredentials 标志相同,但是只有三个值,分别是 omit(默认)、same-origin 和 include。

fetch不支持超时timeout处理

Response

Response 对象通常在 fetch() 的回调中获得,也可以通过 JS 构造,不过这通常只在 ServiceWorkers 中使用。

方法一:单纯setTimeout方式

处理 body

在 Request 和 Response 对象中都可能有 body
属性,并且 body
可以是各种类型,比较复杂,所以前面我们故意先跳过它,在这里单独拿出来讲解。
body
可以是以下任何一种类型的实例:
ArrayBuffer
ArrayBufferView (Uint8Array and friends)
Blob/File
string
URLSearchParams
FormData —— 目前不被 Gecko 和 Blink 支持,Firefox 预计在版本 39 和 Fetch 的其他部分一起推出

var oldFetchfn = fetch; //拦截原始的fetch方法window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法 return new Promise(function(resolve, reject){ var timeoutId = setTimeout(function(){ reject(new Error("fetch timeout")) }, opts.timeout); oldFetchfn(input, opts).then( res={ clearTimeout(timeoutId); resolve(res) }, err={ clearTimeout(timeoutId); reject(err) } ) })}

Fetch和promise不足

1.不能中断,没有 abort、terminate、onTimeout 或 cancel 方法
2.缺少其它一些方法:always,progress,finally
3.没有 Deferred
4.没有获取状态方法:isRejected,isResolved

当然在上面基础上可以模拟类似XHR的abort功能:

var oldFetchfn = fetch; window.fetch = function(input, opts){ return new Promise(function(resolve, reject){ var abort_promise = function(){ reject(new Error("fetch abort")) }; var p = oldFetchfn(input, opts).then(resolve, reject); p.abort = abort_promise; return p; })}

方法二:利用Promise.race方法Promise.race方法接受一个promise实例数组参数,表示多个promise实例中任何一个最先改变状态,那么race方法返回的promise实例状态就跟着改变,具体可以参考这里。

var oldFetchfn = fetch; //拦截原始的fetch方法window.fetch = function(input, opts){//定义新的fetch方法,封装原有的fetch方法 var fetchPromise = oldFetchfn(input, opts); var timeoutPromise = new Promise(function(resolve, reject){ setTimeout(()={ reject(new Error("fetch timeout")) }, opts.timeout) }); retrun Promise.race([fetchPromise, timeoutPromise])}

fetch不支持JSONP

npminstallfetch-jsonp--save-dev

然后在像下面一样使用:

fetchJsonp('/users.jsonp', { timeout: 3000, jsonpCallback: 'custom_callback' }) .then(function(response) { return response.json() }).catch(function(ex) { console.log('parsing failed', ex) })

fetch不支持progress事件

XHR是原生支持progress事件的,例如下面代码这样:

var xhr = new XMLHttpRequest()xhr.open('POST', '/uploads')xhr.onload = function() {}xhr.onerror = function() {}function updateProgress (event) { if (event.lengthComputable) { var percent = Math.round((event.loaded / event.total) * 100) console.log(percent) }xhr.upload.onprogress =updateProgress; //上传的progress事件xhr.onprogress = updateProgress; //下载的progress事件}xhr.send();

但是fetch是不支持有关progress事件的;不过可喜的是,根据fetch的指导规范标准,其内部设计实现了Request和Response类;其中Response封装一些方法和属性,通过Response实例可以访问这些方法和属性,例如response.json()、response.body等等;

值得关注的地方是,response.body是一个可读字节流对象,其实现了一个getRender()方法,其具体作用是:

getRender()方法用于读取响应的原始字节流,该字节流是可以循环读取的,直至body内容传输完成;

因此,利用到这点可以模拟出fetch的progress

// fetch() returns a promise that resolves once headers have been receivedfetch(url).then(response = { // response.body is a readable stream. // Calling getReader() gives us exclusive access to the stream's content var reader = response.body.getReader(); var bytesReceived = 0; // read() returns a promise that resolves when a value has been received reader.read().then(function processResult(result) { // Result objects contain two properties: // done - true if the stream has already given you all its data. // value - some data. Always undefined when done is true. if (result.done) { console.log("Fetch complete"); return; } // result.value for fetch streams is a Uint8Array bytesReceived  = result.value.length; console.log('Received', bytesReceived, 'bytes of data so far'); // Read some more, and call this function again return reader.read().then(processResult); });});

fetch跨域问题

fetch的mode配置项有3个值,如下:

same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque。

原文来自:

本文由yzc216亚洲城发布于网站首页,转载请注明出处:fetch使用的常见问题及其解决办法

关键词: yzc216亚洲城