js_XMLHTTPRequest与referre

XMLHTTPRequest

浏览器与服务器之间采用http协议进行通信,通过在地址栏输入网址向服务器发送请求,AJAX指的是通过JavaScript异步通信从而从服务器中获取xml文档中提取数据,在更新网页相应部分,而不用刷新整个页面。

具体来说AJAX具体分为几个步骤:

  1. 创建XMLHTTPRequest实例

  1. 发出HTTP请求

  1. 接受服务器传回的数据

  1. 更新网页相应位置的数据

换句话来说,就是AJAX通过原来的XMLHTTPRequest对象发送http请求,得到服务器传回的数据再进行处理,使浏览器页面的相应部分进行刷新,而不是刷新整个页面,但是呢AJAX已经不是字面上的意思了,也就是说服务器传回的数据已经是JSON格式的,而不是XML文本格式的了

XMLHTTPRequest对象是AJAX的主要接口,用于浏览器与服务器之间的通信,尽管字面意思上有XML和HTTP,实际上可以可以使用不同的协议,以及传回数据的文本格式也有所改变

实例

XMLHTTPRequest原本是一个构造函数,可以通过new来创建新的实例

var xhr = new XMLHttpRequest();

一旦建立好了新的实例就要通过open建立相应的连接,以及相关的细节问题

xhr.open('GET', 'http://www.example.com/page.php', true);
get为请求,与指定的服务器网址连接进行连接,后边的http://www.example.com/page.php就是指定的服务器网址,最后的参数true指相应的请求为异步

异步请求和同步请求相对,异步不需要等待响应,随时可以发送下一次请求。
如果是同步请求,需要将信息填写完整,再发送请求,服务器响应填写是否正确,再做修改。
但是异步请求是局部页面更新。

然后回调函数进行监听,监听相应通信的状态变化

xhr.onreadystatechange = handleStateChange;

function handleStateChange() {
  // ...
}
相应函数一旦发生变化,就会调用进行监听函数handlestatechange

最后使用send函数进行发送请求

xhr.send(null);
上述代码的参数为null指的是发送请求不携带数据,如果发送post请求,此处就应该携带相应数据,一旦拿到服务器的数据,只更新相应位置,而不是更新整个页面

下面是XMLHTTPRequest对象的简单用法的完整例子

var xhr = new XMLHttpRequest();          //使用new建立新的实例

xhr.onreadystatechange = function(){      //回调函数进行监听,一旦发生变化,监听函数也会变化
  // 通信成功时,状态值为4
  if (xhr.readyState === 4){
    if (xhr.status === 200){
      console.log(xhr.responseText);
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.onerror = function (e) {            
  console.error(xhr.statusText);
};

xhr.open('GET', '/endpoint', true);          //用get进行连接,请求采用的是异步请求
xhr.send(null);          //最后进行send函数发送请求,使相应位置进行变化,而不是刷新整个页面,null请求不携带数据体,使用post请求需要携带相应数据体,则就需要添加相应的参数

XMLHTTPRequest实例的属性

上述XMLHTTPRequrst的实例完整操作中遇到了几个并没有提出的函数,下面会讲述实例的相关属性

XMLHttpRequest.readyState

观察完整实例,会发现XMLHttpRequest.readyState返回一个整数,表示实例对象的当前状态。该属性只读。它可能返回以下值。

  • 0,表示 XMLHttpRequest 实例已经生成,但是实例的open()方法还没有被调用。

  • 1,表示open()方法已经调用,但是实例的send()方法还没有调用,仍然可以使用实例的setRequestHeader()方法,设定 HTTP 请求的头信息。

  • 2,表示实例的send()方法已经调用,并且服务器返回的头信息和状态码已经收到。

  • 3,表示正在接收服务器传来的数据体(body 部分)。这时,如果实例的responseType属性等于text或者空字符串,responseText属性就会包含已经收到的部分信息。

  • 4,表示服务器返回的数据已经完全接收,或者本次接收已经失败。

通信过程中,每当实例对象发生状态变化,它的readyState属性的值就会改变。这个值每一次变化,都会触发readyStateChange事件。

var xhr = new XMLHttpRequest();

if (xhr.readyState === 4) {
  // 请求结束,处理服务器返回的数据,表示服务器返回的数据已经完全接受或者接收失败
} else {
  // 显示提示“加载中……”这里表示http请求还在进行中
}

XMLHttpRequest.onreadystatechange

这个属于一个监听函数,就是通过监听到通信状态的变化,监听函数也发生相应的变化,readystate发生变化就会触发这个属性,另外,如果使用实例的abort()方法,终止 XMLHttpRequest 请求,也会造成readyState属性变化,导致调用XMLHttpRequest.onreadystatechange属性。

var xhr = new XMLHttpRequest();       //使用new创建新的实例
xhr.open( 'GET', 'http://example.com' , true );   //建立相应连接,采用异步请求
xhr.onreadystatechange = function () {            //监听,通过readystate返回相应的值判断通信状态
  if (xhr.readyState !== 4 || xhr.status !== 200) {
    return;
  }
  console.log(xhr.responseText);
};
xhr.send();            //通过send函数发送请求

XMLHttpRequest.response

此属性表示服务器返回的数据体,可以是任何类型,比如字符串、二进制对象等等,具体类型由responseType属性来决定,该属性也为只读

如果本次请求没有成功或者数据不完整,该属性等于null。但是,如果responseType属性等于text或空字符串,在请求没有结束之前(readyState等于3的阶段),response属性包含服务器已经返回的部分数据。

var xhr = new XMLHttpRequest();          //使用new创建实例

xhr.onreadystatechange = function () {     //监听,如果实例对象状态返回值为4,返回相应的数据体
  if (xhr.readyState === 4) {
    handler(xhr.response);
  }
}

XMLHttpRequest.responseType

服务器返回数据体的属性类型就是通过这个属性来决定的,这个属性是可写的,可以在调用open()方法之后、调用send()方法之前,设置这个属性的值,告诉浏览器如何解读返回的数据。如果responseType设为空字符串,就等同于默认值text。

  • ""(空字符串):等同于text,表示服务器返回文本数据。

  • "arraybuffer":ArrayBuffer 对象,表示服务器返回二进制数组。

  • "blob":Blob 对象,表示服务器返回二进制对象。

  • "document":Document 对象,表示服务器返回一个文档对象。

  • "json":JSON 对象。

  • "text":字符串。

text类型适合大多数情况,而且直接处理文本也比较方便。document类型适合返回 HTML / XML 文档的情况,这意味着,对于那些打开 CORS(跨域) 的网站,可以直接用 Ajax 抓取网页,然后不用解析 HTML 字符串,直接对抓取回来的数据进行 DOM 操作。blob类型适合读取二进制数据,比如图片文件为二进制数据

var xhr = new XMLHttpRequest();     //创建实例
xhr.open('GET', '/path/to/image.png', true);    //建立连接异步请求
xhr.responseType = 'blob';                      //返回数据类型设置为blob类型适合读取二进制数据

xhr.onload = function(e) {
  if (this.status === 200) {
    var blob = new Blob([xhr.response], {type: 'image/png'});
    // 或者
    var blob = xhr.response;
  }
};

xhr.send();         //发送请求

设置数据类型是为了使返回的数据得到相应的格式然后再进行处理

XMLHttpRequest.responseText

XMLHttpRequest.responseText属性返回从服务器接收到的字符串,该属性为只读。只有 HTTP 请求完成接收以后,该属性才会包含完整的数据。

var xhr = new XMLHttpRequest();        // 创建实例
xhr.open('GET', '/server', true);     //建立连接,异步请求

xhr.responseType = 'text';          //返回的数据类型设置为text
xhr.onload = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseText);         
  }
};

xhr.send(null);        //发送请求

XMLHttpRequest.responseXML

XMLHttpRequest.responseXML属性返回从服务器接收到的 HTML 或 XML 文档对象,该属性为只读。如果本次请求没有成功,或者收到的数据不能被解析为 XML 或 HTML,该属性等于null。

该属性生效的前提是 HTTP 回应的Content-Type头信息等于text/xml或application/xml。这要求在发送请求前,XMLHttpRequest.responseType属性要设为document。如果 HTTP 回应的Content-Type头信息不等于text/xml和application/xml,但是想从responseXML拿到数据(即把数据按照 DOM 格式解析),那么需要手动调用XMLHttpRequest.overrideMimeType()方法,强制进行 XML 解析。

var xhr = new XMLHttpRequest();       //创建实例
xhr.open('GET', '/server', true);     //建立连接异步请求

xhr.responseType = 'document';       //设置返回数据类型
xhr.overrideMimeType('text/xml');    //HTTP 回应的Content-Type头信息等于text/xml

xhr.onload = function () {           //触发监听函数
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(xhr.responseXML);
  }
};

xhr.send(null);                     //发送请求

XMLHttpRequest.responseURL

XMLHttpRequest.responseURL属性是字符串,表示发送数据的服务器的网址。

var xhr = new XMLHttpRequest();     //创建实例
xhr.open('GET', 'http://example.com/test', true);//建立连接异步请求
xhr.onload = function () {         
  // 返回 http://example.com/test
  console.log(xhr.responseURL);
};
xhr.send(null);       //发送请求

注意,这个属性的值与open()方法指定的请求网址不一定相同。如果服务器端发生跳转,这个属性返回最后实际返回数据的网址。另外,如果原始 URL 包括锚点(fragment),该属性会把锚点剥离。

XMLHttpRequest.status

XMLHttpRequest.statusText

XMLHttpRequest.status属性返回一个整数,表示服务器回应的 HTTP 状态码。一般来说,如果通信成功的话,这个状态码是200;如果服务器没有返回状态码,那么这个属性默认是200。请求发出之前,该属性为0。该属性只读。

  • 200, OK,访问正常

  • 301, Moved Permanently,永久移动

  • 302, Moved temporarily,暂时移动

  • 304, Not Modified,未修改

  • 307, Temporary Redirect,暂时重定向

  • 401, Unauthorized,未授权

  • 403, Forbidden,禁止访问

  • 404, Not Found,未发现指定网址

  • 500, Internal Server Error,服务器发生错误

基本上,只有2xx和304的状态码,表示服务器返回是正常状态。

if (xhr.readyState === 4) {
  if ( (xhr.status >= 200 && xhr.status < 300)
    || (xhr.status === 304) ) {           // 处理服务器的返回数据
  } else {       // 出错
  }
}

XMLHttpRequest.statusText属性返回一个字符串,表示服务器发送的状态提示。

不同于status属性,该属性包含整个状态信息,比如“OK”和“Not Found”。

在请求发送之前(即调用open()方法之前),该属性的值是空字符串;如果服务器没有返回状态提示,该属性的值默认为“OK”。该属性为只读属性。

XMLHttpRequest.timeout

XMLHttpRequestEventTarget.ontimeout

XMLHttpRequest.timeout属性返回一个整数,表示多少毫秒后,如果请求仍然没有得到结果,就会自动终止。如果该属性等于0,就表示没有时间限制。

XMLHttpRequestEventTarget.ontimeout属性用于设置一个监听函数,如果发生 timeout 事件,就会执行这个监听函数。

var xhr = new XMLHttpRequest();      //创建实例
var url = '/server';

xhr.ontimeout = function () {        //设置时间限制,在多久没有得到结果就自动终止
  console.error('The request for ' + url + ' timed out.');
};

xhr.onload = function() {            //设置返回数据
  if (xhr.readyState === 4) {
    if (xhr.status === 200) {
      // 处理服务器返回的数据
    } else {
      console.error(xhr.statusText);
    }
  }
};

xhr.open('GET', url, true);          //创建连接异步请求
// 指定 10 秒钟超时
xhr.timeout = 10 * 1000;            //设置超时时间
xhr.send(null);                     //发送请求

事件监听属性

XMLHttpRequest 对象可以对以下事件指定监听函数。

  • XMLHttpRequest.onloadstart:loadstart 事件(HTTP 请求发出)的监听函数

  • XMLHttpRequest.onprogress:progress事件(正在发送和加载数据)的监听函数

  • XMLHttpRequest.onabort:abort 事件(请求中止,比如用户调用了abort()方法)的监听函数

  • XMLHttpRequest.onerror:error 事件(请求失败)的监听函数

  • XMLHttpRequest.onload:load 事件(请求成功完成)的监听函数

  • XMLHttpRequest.ontimeout:timeout 事件(用户指定的时限超过了,请求还未完成)的监听函数

  • XMLHttpRequest.onloadend:loadend 事件(请求完成,不管成功或失败)的监听函数

progress事件的监听函数有一个事件对象参数,该对象有三个属性:loaded属性返回已经传输的数据量,total属性返回总的数据量,lengthComputable属性返回一个布尔值,表示加载的进度是否可以计算。所有这些监听函数里面,只有progress事件的监听函数有参数,其他函数都没有参数

注意,如果发生网络错误(比如服务器无法连通),onerror事件无法获取报错信息。也就是说,可能没有错误对象,所以这样只能显示报错的提示。

XMLHttpRequest.withCredentials

XMLHttpRequest.withCredentials属性是一个布尔值,表示跨域请求时,用户信息(比如 Cookie 和认证的 HTTP 头信息)是否会包含在请求之中,默认为false,发送跨域请求时不会发送本机设置的cookie

如果需要跨域 AJAX 请求发送 Cookie,需要withCredentials属性设为true。注意,同源的请求不需要设置这个属性。

为了让这个属性生效,服务器必须显式返回Access-Control-Allow-Credentials这个头信息。

Access-Control-Allow-Credentials: true

withCredentials属性打开的话,跨域请求不仅会发送 Cookie,还会设置远程主机指定的 Cookie。

withCredentials属性没有打开,那跨域的 AJAX 请求即使明确要求浏览器设置 Cookie,浏览器也会忽略

注意,脚本总是遵守同源政策,无法从document.cookie或者 HTTP 回应的头信息之中,读取跨域的 Cookie,withCredentials属性不影响这一点。

XMLHttpRequest.upload

XMLHttpRequest 不仅可以发送请求,还可以发送文件,这就是 AJAX 文件上传。发送文件以后,通过XMLHttpRequest.upload属性可以得到一个对象,通过观察这个对象,可以得知上传的进展。主要方法就是监听这个对象的各种事件:loadstart、loadend、load、abort、error、progress、timeout。

通过这个对象来研究关于他相关的所有时间,监听这个对象的所有事件


XMLHttpRequest 的实例方法

XMLHttpRequest.open()

关于open的参数有5种

  • method:表示 HTTP 动词方法,比如GET、POST、PUT、DELETE、HEAD等。

  • url: 表示请求发送目标 URL。

  • async: 布尔值,表示请求是否为异步,默认为true。如果设为false

则send()方法只有等到收到服务器返回了结果,才会进行下一步操作。该参数可选。由于同步 AJAX 请求会造成浏览器失去响应,许多浏览器已经禁止在主线程使用,只允许 Worker 里面使用。所以,这个参数轻易不应该设为false。

  • user:表示用于认证的用户名,默认为空字符串。该参数可选。

  • password:表示用于认证的密码,默认为空字符串。该参数可选。

注意,如果对使用过open()方法的 AJAX 请求,再次使用这个方法,等同于调用abort(),即终止请求。

XMLHttpRequest.send()

XMLHttpRequest.send()方法用于实际发出 HTTP 请求。它的参数是可选的,如果不带参数,就表示 HTTP 请求只有一个 URL,没有数据体,典型例子就是 GET 请求;如果带有参数,就表示除了头信息,还带有包含具体数据的信息体,典型例子就是 POST 请求。

GET
var xhr = new XMLHttpRequest();
xhr.open('GET',
  'http://www.example.com/?id=' + encodeURIComponent(id),
  true
);
xhr.send(null);

GET请求的参数,作为查询字符串附加在 URL 后面

POST
var xhr = new XMLHttpRequest();
var data = 'email='
  + encodeURIComponent(email)
  + '&password='
  + encodeURIComponent(password);

xhr.open('POST', 'http://www.example.com', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send(data);

所有的监听事件都要在send前完成

send方法的参数就是发送的数据。多种格式的数据,都可以作为它的参数。

可以在data前面加上相应的发送数据类型

如果send()发送 DOM 对象,在发送之前,数据会先被串行化。如果发送二进制数据,最好是发送ArrayBufferView或Blob对象,这使得通过 Ajax 上传文件成为可能。

FormData对象
构造表单数据
加工表单数据

XMLHttpRequest.setRequestHeader()

XMLHttpRequest.setRequestHeader()方法用于设置浏览器发送的 HTTP 请求的头信息该方法必须在open()之后、send()之前调用。如果该方法多次调用,设定同一个字段,则每一次调用的值会被合并成一个单一的值发送。

该方法接受两个参数。第一个参数是字符串,表示头信息的字段名,第二个参数是字段值。

xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Content-Length', JSON.stringify(data).length);
xhr.send(JSON.stringify(data));

设置头信息的类型以及数据长度

XMLHttpRequest.overrideMimeType()

XMLHttpRequest.overrideMimeType()方法用来指定 MIME 类型,覆盖服务器返回的真正的 MIME 类型,从而让浏览器进行不一样的处理。举例来说,服务器返回的数据类型是text/xml,由于种种原因浏览器解析不成功报错,这时就拿不到数据了。为了拿到原始数据,我们可以把 MIME 类型改成text/plain,这样浏览器就不会去自动解析,从而我们就可以拿到原始文本了。

注意,该方法必须在send()方法之前调用。

修改服务器返回的数据类型,不是正常情况下应该采取的方法。如果希望服务器返回指定的数据类型,可以用responseType属性告诉服务器,只有在服务器无法返回某种数据类型时,才使用overrideMimeType()方法。

var xhr = new XMLHttpRequest();
xhr.onload = function(e) {
  var arraybuffer = xhr.response;
  // ...
}
xhr.open('GET', url);
xhr.responseType = 'arraybuffer';
xhr.send();
在这种情况下才可以采用这种办法来告诉服务器数据类型,尽可能使用responsetype来告诉服务器数据类型

XMLHttpRequest.getResponseHeader()

XMLHttpRequest.getResponseHeader()方法返回 HTTP 头信息指定字段的值,如果还没有收到服务器回应或者指定字段不存在,返回null。该方法的参数不区分大小写。如果有多个字段同名,它们的值会被连接为一个字符串,每个字段之间使用“逗号+空格”分隔。

function getHeaderTime() {
  console.log(this.getResponseHeader("Last-Modified"));
}

var xhr = new XMLHttpRequest();
xhr.open('HEAD', 'yourpage.html');
xhr.onload = getHeaderTime;
xhr.send();

XMLHttpRequest.abort()

XMLHttpRequest.abort()方法用来终止已经发出的 HTTP 请求。调用这个方法以后,readyState属性变为4,status属性变为0。

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example.com/page.php', true);
setTimeout(function () {
  if (xhr) {
    xhr.abort();
    xhr = null;
  }
}, 5000);
发出请求5秒后,终止请求

XMLHttpRequest 实例的事件

readyStateChange 事件

readyState属性的值发生改变,就会触发 readyStateChange 事件。

我们可以通过onReadyStateChange属性,指定这个事件的监听函数,对不同状态进行不同处理。尤其是当状态变为4的时候,表示通信成功,这时回调函数就可以处理服务器传送回来的数据。

progress 事件

上传文件时,XMLHttpRequest 实例对象本身和实例的upload属性,都有一个progress事件,会不断返回上传的进度。

load 事件、error 事件、abort 事件

load 事件表示服务器传来的数据接收完毕,error 事件表示请求出错,abort 事件表示请求被中断(比如用户取消请求)。

loadend 事件

abort、load和error这三个事件,会伴随一个loadend事件,表示请求结束,但不知道其是否成功。

timeout 事件

服务器超过指定时间还没有返回结果,就会触发 timeout 事件,具体的例子参见timeout属性一节。

Referer

Referer是 HTTP请求header 的一部分,当浏览器(或者模拟浏览器行为)向web 服务器发送请求的时候,头信息里有包含 referer。比如我在http://www.google.com 里有一个http://www.google.com 链接,那么点击这个http://www.google.com ,它的header 信息里就有:

Referer=http://www.google.com

服务端一般使用Referer(注:正确英语拼写应该是referrer,由于早期HTTP规范的拼写错误,为了保持向后兼容就一直延续下来)请求头识别访问来源,可能会以此统计分析、日志记录以及缓存优化等。

防盗链

Referer=http://www.google.com

可以通过这个进行防盗链,比如我只允许我自己网站访问自己的服务器,那么每次访问前会通过referer进行对比是不是自己的网址,如果是就可以访问,如果不是就进行拦截

将这个http请求发给服务器后,如果服务器要求必须是某个地址或者某几个地址才能访问,而你发送的referer不符合他的要求,就会拦截或者跳转到他要求的地址,然后再通过这个地址进行访问

盗链是指在自己的页面上展示一些并不在自己服务器上的一些内容, 获取别人的资源地址,绕过别人的资源展示页面,直接在自己的页面上向最终用户提供此内容。 一般被盗链的都是图片、 可执行文件、 音视频文件、压缩文件等资源。通过盗链的手段可以减轻自己服务器的负担

防止恶意请求

比如静态请求是*.html结尾的,动态请求是*.shtml,那么由此可以这么用,所有的*.shtml请求,必须 Referer 为我自己的网站。

空Referer是怎么回事?什么情况下会出现Referer?
首先,我们对空 Referer  的定义为, Referer  头部的内容为空,或者,一个 HTTP  请求中根本不包含 Referer头部。
那么什么时候 HTTP  请求会不包含 Referer  字段呢?根据Referer的定义,它的作用是指示一个请求是从哪里链接过来,那么当一个请求并不是由链接触发产生的,那么自然也就不需要指定这个请求的链接来源。
比如,直接在浏览器的地址栏中输入一个资源的URL地址,那么这种请求是不会包含 Referer  字段的,因为这是一个“凭空产生”的 HTTP  请求,并不是从一个地方链接过去的。
那么在防盗链设置中,允许空Referer和不允许空Referer有什么区别?
允许 Referer  为空,意味着你允许比如浏览器直接访问,就是空。

绕过防盗链

那么现在的很多网站是如何利用referer来进行防图片盗链的呢?三种情况下允许引用图片:

  1. 本网站。

  1. 无referer信息的情况。(服务器认为是从浏览器直接访问的图片URL,所以这种情况下能正常访问)

  1. 授权的网址。

设置meta以及referrerpolicy

Referrer-policy

Referrer-policy作用就是为了控制请求头中referer的内容

包含以下值:

  • no-referrer : 整个referee首部会被移除,访问来源信息不随着请求一起发送。

  • no-referrer-when-downgrade : 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP).

  • origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅发送文件的源。

  • same-origin: 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。

  • strict-origin: 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)。

  • strict-origin-when-cross-origin: 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。

  • unsafe-url: 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址。(最不安全了)

设置referer
  1. 在HTML里设置meta

<meta name="referrer" content="origin">
  1. 或者用<a>、<area>、<img>、<iframe>、<script> 或者 <link> 元素上的 referrerpolicy 属性为其设置独立的请求策略。

<script src='/javascripts/test.js' referrerpolicy="no-referrer"></script>

利用iframe伪造请求referer

const putNoRefererImage = (() => {
    let iframe
    /*
        src: 图片地址
        wrap:需要加载图片的容器
     */
    return function (src, wrap) {
        if (iframe) {
            iframe.remove()
        }
 
        let url = new URL(src);
        let frameid = 'frameimg' + Math.random();
        window.img = `<img id="tmpImg" width=400 src="${url}" alt="图片加载失败,请稍后再试"/> `;
 
        // 构造一个iframe
        iframe = document.createElement('iframe')
        iframe.id = frameid
        iframe.src = "javascript:parent.img;" // 通过内联的javascript,设置iframe的src
        // 校正iframe的尺寸,完整展示图片
        iframe.onload = function () {
            var img = iframe.contentDocument.getElementById("tmpImg")
            if (img) {
                iframe.height = img.height + 'px'
                iframe.width = img.width + 'px'
            }
        }
        iframe.width = 200
        iframe.height = 200
        iframe.scrolling = "no"
        iframe.frameBorder = "0"
        wrap.appendChild(iframe)
    }
})();
 
putNoRefererImage(imgSrc, document.body);

客户端在请求时修改header头部

XMLHTTPRequest

XMLHttpRequest中setRequestHeader方法,用于向请求头添加或修改字段

// 通过ajax下载图片
function loadImage(uri) {
    return new Promise(resolve => {
        let xhr = new XMLHttpRequest();
        xhr.responseType = "blob";
        xhr.onload = function() {
            resolve(xhr.response);
        };
​
        xhr.open("GET", uri, true);
        // 通过setRequestHeader设置header不会生效
        // 会提示 Refused to set unsafe header "Referer"
        xhr.setRequestHeader("Referer", ""); 
        xhr.send();
    });
}
  
​
// 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
function handleBlob(blob) {
    let reader = new FileReader();
    reader.onload = function(evt) {
        let img = document.createElement('img');
        img.src = evt.target.result;
        document.getElementById('container').appendChild(img)
    };
    reader.readAsDataURL(blob);
}
​
const imgSrc = "https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg";
​
loadImage(imgSrc).then(blob => {
    handleBlob(blob);
});

上述代码运行时会发现控制台提示错误:

Refused to set unsafe header "Referer"

可以看见setRequestHeader设置referer响应头是无效的,这是由于浏览器为了安全起见,无法手动设置部分保留字段,不幸的是Referer恰好就是保留字段之一

fetch
// 将下载下来的二进制大对象数据转换成base64,然后展示在页面上
function handleBlob(blob) {
    let reader = new FileReader();
    reader.onload = function(evt) {
        let img = document.createElement('img');
        img.src = evt.target.result;
        document.getElementById('container').appendChild(img)
    };
    reader.readAsDataURL(blob);
}

const imgSrc = "https://tiebapic.baidu.com/forum/w%3D580%3B/sign=f88eb0f2cf82b9013dadc33b43b6ab77/562c11dfa9ec8a135455cc35b203918fa1ecc09c.jpg";


function fetchImage(url) {
    return fetch(url, {
        headers: {
            // "Referer": "", // 这里设置无效
        },
        method: "GET",  
        referrer: "", // 将referer置空
        // referrerPolicy: 'no-referrer', 
    }).then(response => response.blob());
}

fetchImage(imgSrc).then(blob => {
    handleBlob(blob);
});

通过将配置参数referrer置空,可以看见本次请求已经不带referer了,或者设置 referrerPolicy为"no-referrer"

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值