如何拦截响应内容并修改响应头

news2024/11/18 13:36:28

背景及需求描述

背景

记录分享下近期遇到并解决的困扰了比较久的问题:在不同系统微信生态发现同一个cos地址用window.open(url)打开在苹果和安卓设备的微信生态上表现不一致:对于文档类型,响应头Content-Type: application/pdf 在安卓微信上唤起浏览器下载弹窗;在ios微信、
safari等浏览器中则直接打开了该文档[出现这种差异的原因有懂的大佬欢迎指教~]。

要在ios设备中实现下载,需配置响应头Content-Type: application/octet-stream,但是该响应头在安卓微信内变成无法下载,提示“可在浏览器打开此网页来下载文件” 。

目前生产环境文档资源配置响应头Content-Type: application/octet-stream(为解决ios设备无法下载pdf), 最后是联系云厂商根据设备信息定制响应头解决了问题。但是在不直接向用户暴露出资源链接的情况下,前端其实也是可以通过“修改响应信息”达到相同效果的。

目标效果:
ios微信:

android微信:

浏览器弹出自带的下载确认弹窗
基于现状,我们的目的是:如何修改,实现安卓手机的微信中可以唤起跳转到浏览器的弹窗。

需求

我们知道: 用window.open(url)访问资源其实就是浏览器向该url指向的服务器发了个get请求,我们能用这种方法下载文件是因为:当使用window.open(url)时,浏览器会新开一个窗口或标签页(没带_self的情况)并尝试对给定的url进行导航/渲染,如果url指向的是个文件并且正确配置了响应头,浏览器会尝试直接下载该文件。
在这里插入图片描述

上面说的响应头包括:

Content-Type

Content-Type 实体头部用于指示资源的 MIME 类型 media type 。在响应中,Content-Type 标头告诉客户端实际返回的内容的内容类型。浏览器会在某些情况下进行 MIME 查找,并不一定遵循此标题的值; 为了防止这种行为,可以将标题 X-Content-Type-Options 设置为 nosniff。(引用自MDN)

这个响应头是用来告诉浏览器返回的内容是什么类型的。例如,它可以是“text/html”,表示内容是HTML文档;或者“image/jpeg”,表示内容是一张JPEG图片;

Content-Type: application/octet-stream告诉浏览器:服务器发送的响应内容是二进制格式的。它是一种通用的二进制文件类型,可以用于任何类型的二进制文件,包括文本文件、图片、音频、视频等。但是,由于它没有特定的子类型,因此它通常被视为应用程序文件的默认值,但在实际应用中很少直接使用。
Content-Type: application/pdf则是一种确定的二进制文件类型,用于指示服务器发送的响应内容是PDF格式的文件。当Content-Type设置为application/pdf时,浏览器会知道响应的内容是PDF文件,并采取相应的措施来正确地显示和渲染该文件(看浏览器能力,是解析还是下载,如果浏览器有内置pdf解析器或有解析插件就会解析该文档然后渲染文档内容)。

这个头部信息并不会直接决定内容是否被下载,而是告诉浏览器这是哪一种媒体类型,如何处理和显示这个内容,取决于浏览器本身的处理程序。
总结:这是个啥啥啥,怎么处理你浏览器自己决定。

Content-Disposition

在常规的 HTTP 应答中,Content-Disposition 响应标头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。(引用自MDN)

这个响应头告诉浏览器该如何处理返回的内容。 它可以被设置为“inline”或者“attachment”。一般情况下,当设置为“inline”时,内容通常会直接显示在浏览器窗口中(例如,图片或PDF文件)。而当设置为“attachment”时,浏览器通常会提示用户下载内容,而不是直接在浏览器窗口中显示。

总结:我要浏览器怎么怎么做。

也就是说,上述两个响应头影响了不同系统和浏览器中访问文件地址的表现,而我们在“背景”部分也介绍清楚了如何修改可以实现我们的需求,接下来便是实践部分:

显然我们直接用window.open(url)(或者直接用下面提到的a标签)的方法不能去拦截http请求/响应,当我们主动用这个url发个get请求就可以对请求和响应进行拦截,并做出相应的修改。下面我们拆解下任务:

  1. 实现前端发送一个http请求并下载资源;
  2. 拦截并改写http的response Header 【生产环境服务器配置响应头是Content-Type: application/otect-stream,因此修改安卓设备的响应头为Content-Type: application/pdf即可】

发送http请求并下载资源

需要注意的是,不管用哪一种请求库向资源发起请求这个动作本身并不会将文件下载到本地,我们只是主动像服务器发了个http请求、获取到响应的数据。我们需要额外的处理程序下载文件——常见的方法是创建一个a标签,设置其href属性为该资源url,并设置download属性,然后去触发它的点击事件。
设置了download属性的a标签click()事件被触发后,会默认导航到href属性所指向的url,也就是向url所指向的服务器发送请求。这是浏览器代理的,用其内置的下载机制。

请求到数据后,我们需要将返回的内容存放到一个容器里,然后创建一个url指向这个容器,这样我们需要用的时候就可以访问到而不用重复向服务器请求。下面我们将会用到js的Blob对象来充当这个容器,将response的内容转换成二进制数据放到这个blob实例对象中,并用URL.createObjectURL(blob)创建一个指向这个容器的变量(一个URL),在下载的时候直接用这个URL。

xhr方式

let url = "https://xxx?download_name=%23%E6%AF%8F%E6%97%A5%E7%9C%8B%E7%82%B92022.01.01.pdf";
            fetch(url)
            .then(response => {
                return response.blob();
            })  
            .then(blob => {   // 处理二进制数据  
                const url = URL.createObjectURL(blob); //创建指向Blob对象实例的url
                //下面是下载程序
                const link = document.createElement('a');  
                link.href = url;  
                link.setAttribute('download', 'file.pdf'); // 下载文件的文件名  
                document.body.appendChild(link);  
                link.click();  
                document.body.removeChild(link);  
            })  
            .catch(error => console.error('下载文件时发生错误:', error));

axios方式

      const instance = axios.create();
            instance.get(url, { responseType: 'blob' }) 
                .then(function (response) {  
                    // 处理二进制数据  
                    const blob = response.data;
                    const url = window.URL.createObjectURL(blob); 
                    const link = document.createElement('a');  
                    link.href = url;  
                    link.setAttribute('download', 'file.pdf'); // 设置文件名  
                    document.body.appendChild(link);  
                    link.click();  
                    setTimeout(() => {
                        link.remove()
                    }, 0)
                })  
                .catch(function (error) {  
                    // 处理错误...
                });

我们已经实现了第一步的目标,并将该pdf下载到本地,下一步便是修改响应头。

修改响应头实现需求

我们知道前端请求库如axios有提供拦截器(interceptors),可以修改请求参数、请求标头、response的数据格式等。Fetch API也提供了修改请求头的方法:

//修改请求头
let headers = new Headers();  
headers.append('Content-Type', 'image/jpeg');  
headers.append('Another-Header', 'Another-Value');
fetch(url, {  
  method: 'GET',  
  headers: headers,  
  body: data  
})

经过多方实践发现,哪怕用拦截器,也只能修改响应体也就是返回的数据(如格式化一下、增加以下自定义属性等等,更多应用敬请自行探索),并不能直接拦截并修改响应头,对于额外设置的自定义response Header也不会生效

instance.interceptors.response.use(  
  response => {  
    // 对响应数据做点什么  
    response.headers['content-type'] = 'application/pdf'; 
    response.headers['12-1'] = 'application/pdf'; 
    return response;  
  },  
  error => {  
   return Promise.reject(error);  
  }  
); 

当我们尝试求改请求头content-type的值时会出现错误:TypeError: Failed to execute ‘set’ on ‘Headers’: Headers are immutable。
因为,在 Fetch API和Axios中,一旦请求被发送,就不能更改请求头。
得,看起来白忙活一场了,改不了一点。。。


当我尝试了解js的Blob对象,事情迎来了转机!!!
Blob对象包含两个只读属性size和type,这个type啊,是个字符串,表明该 Blob 对象所包含数据的 MIME 类型。如果类型未知,则该值为空字符串。emmmmmm MIME类型。。。不正和我们要修改的Content-Type对应上了嘛!愣着干啥,开干!


等等,只读属性。。。

改不了,改不了一点。。。
不死心又菜菜的我,还是去问ai
在这里插入图片描述

复制对象?好办法!我们拿到了响应的数据,搞个新的Blob对象,至于MIME类型重设下,触发下载的时候骗过浏览器不就好了嘛

const instance = axios.create();
instance.get(url, { responseType: 'blob' })  
  .then(function (response) { 
    // 处理二进制数据  
    const blob = response.data;
    let newBlob = new Blob([blob], {type: 'application/pdf'}); // 重点在这里,在这里可对二进制数据进行处理,比如创建一个可下载的链接  
    const url = window.URL.createObjectURL(newBlob); //important!!!
    const link = document.createElement('a');  
    link.href = url;  
    link.setAttribute('download', 'file.pdf'); // 设置文件名  
    document.body.appendChild(link);  
    link.click();  
    setTimeout(() => {
        link.remove();
        URL.revokeObjectURL(url);  // 释放内存
    }, 0)
})  
.catch(function (error) {  
    // 处理错误  
    console.error(error);  
});

至此,我们已经实现了目标需求,在安卓微信上也可唤起跳转到浏览器的弹窗啦。
如前所述,这种方法适用于不暴露cos链接的情况,如果直接复制cos链接到安卓微信打开还是会弹出toast提示到浏览器去下载,这时候就需要去修改服务器的响应头了。去掉响应头中Content-Disposition的Utf-8也可绕过,看起来是不同浏览器接受对响应头中中文编码设置方式的差异造成的, 详见 https://stackoverflow.com/questions/18050718/utf-8-encoding-name-in-downloaded-file)

完结撒花,如有谬误,敬请指出~

关注我,一起学习前端知识【后面会介绍同时下载大量级文件、大文件如何处理,敬请期待~】
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1115421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nessus安装使用过程

文章目录 一、获取激活码二、获得挑战码三、获得插件包四、复制插件包五、登录设置六、离线激活七、破解IP限制八、解压插件包 一、获取激活码 https://zh-cn.tenable.com/products/nessus/nessus-essentials 通过邮箱获得激活码 ZYSZ-LXWP-QJHV-VPU5-4ZRR 二、获得挑战码 …

提升医院安全的关键利器——医院安全(不良)事件报告系统源码

医院是人们寻求医疗服务和康复的场所,安全是医院运营的基石。然而,医疗过程中不可避免地会出现不良事件,如药物错误、手术事故等。为了及时发现、评估和解决这些问题,医院安全(不良)事件报告系统应运而生。…

Windows 的 PowerShell 提供了哪些命令

本心、输入输出、结果 文章目录 Windows 的 PowerShell 提供了哪些命令前言Microsoft Windows PowerShell 简介Microsoft Windows PowerShell 命令分门别类获得 Microsoft Windows PowerShell 的文档Windows 的 PowerShell 提供了哪些命令,我们列举一下弘扬爱国精神…

YOLOv8改进实战 | 更换主干网络Backbone(三)之轻量化模型ShuffleNetV2

前言 轻量化网络设计是一种针对移动设备等资源受限环境的深度学习模型设计方法。下面是一些常见的轻量化网络设计方法: 网络剪枝:移除神经网络中冗余的连接和参数,以达到模型压缩和加速的目的。分组卷积:将卷积操作分解为若干个…

2023 中国 VR 50 强企业名单发布;OpenAI 新模型性能远低于预期丨 RTE 开发者日报 Vol.71

开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE (Real Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有…

低代码软件:开发老手的新选择?

数字化的来临使得低代码进入人们的视野。它成本低、灵活度高、操作难度小、开发周期短,这些优势把他带入到了众多企业中。有些人可能会有疑问,开发老手可以用低代码软件吗?会不会不好用?今天这篇文章就来为您揭秘! 先…

c++数组教程

今天来讲讲数组 什么是数组&#xff1a; 我们来看一道题目&#xff1a; 【题目部分】 输入n个数,逆序输出它们. 输入 第一行一个整数n (0 < n < 60 ) 第二行n个整数 输出 一行,n个整数。 输入样例 1 5 1 6 2 8 4 输出样例 1 4 8 2 6 1 这下就难住很多人了&…

单点登录与网络犯罪生态系统

这不仅仅是你的感觉&#xff0c;网络犯罪正以惊人的速度增长。在Flare&#xff0c;我们发现2023年的数据勒索勒索软件攻击比2022年增加了112&#xff05;&#xff0c;并且网络犯罪生态系统的活动也在不断增加。 导语&#xff1a;网络犯罪的惊人增长 网络犯罪在当今社会中变得越…

分布式事务无压力:轻松整合Spring Cloud与Alibaba Seata,事务管理so easy

今天我们聊聊在SpringCloud微服务架构中如何解决分布式事务这一技术难题&#xff0c;本文主要包含三个方面内容&#xff1a; 讲解分布式事务的解决方案&#xff1b;介绍 Alibaba Seata 分布式事务中间件&#xff1b;分析 Seata 的 AT 模式实现原理。 分布式事务的解决方案 下…

专业修图软件 Affinity Photo 2 mac中文版编辑功能

Affinity Photo for Mac是应用在MacOS上的专业修图软件&#xff0c;支持多种文件格式&#xff0c;包括psD、PDF、SVG、Eps、TIFF、JPEG等。 Affinity Photo mac提供了许多高级图像编辑功能&#xff0c;如无限制的图层、非破坏性操作、高级的选择工具、高级的调整层、HDR合成、全…

2023最新Google play 开发者账号注册矩阵批量,提高注册成功率需要注意什么?

Google play作为全球最大的应用市场&#xff0c;是很多开发者推广应用的首选平台。不过&#xff0c;不少开发者会通过上传多个马甲包的方式来抢占更多的市场份额&#xff0c;获得更多的收益。 但上传马甲包的行为严重扰乱了Google play的市场环境&#xff0c;是不被谷歌官方允许…

MySQL基本操作之修改表结构

1、末尾增加字段 在表结构末尾增加一个名为 beizhu 的字段,类型为 varchar(250),并添加注释 trie: ALTER TABLE student ADD beizhu VARCHAR(250) COMMENT trie; 2、在表结构开头增加一个名为 xxx 的字段,类型为 varchar(20): ALTER TABLE student ADD xxx VARCHAR(20)…

4 OpenCV实现多目三维重建(多张图片增量式生成稀疏点云)【附源码】

本文是基于 OpenCV4.80 进行的&#xff0c;关于环境的配置可能之后会单独说&#xff0c;先提一嘴 vcpkg 真好用 1 大致流程 从多张图片逐步生成稀疏点云&#xff0c;这个过程通常包括以下步骤&#xff1a; 初始重建&#xff1a; 初始两张图片的选择十分重要&#xff0c;这是整…

nginx+nodejs 一台服务器站架多个网站

一、一台服务器架设多个 nodejs 网站的拓扑结构 二、搭建 Nodejs 生产环境 1、下载 下载 nodejs 二进制代码包或者&#xff0c;然后减压到 /usr/local/nodejs 2、配置环境变量 (1).vi /etc/profile (2).最后面添加&#xff1a; export NODE_HOME/usr/local/nodejs/bin…

常见问题-找不到vcruntime140.dll无法继续执行代码解决方案

本文将介绍五种不同的解决方案&#xff0c;帮助大家解决这个问题。 首先&#xff0c;我们需要了解为什么会出现找不到vcruntime140.dll的情况。这种情况通常是由于以下几个原因导致的&#xff1a; 1. 系统环境变量设置不正确&#xff1a;系统环境变量中可能没有包含vcruntime…

卷积神经网络CNN学习笔记

目录 1.全连接层存在的问题2.卷积运算3.填充(padding)3.1填充(padding)的意义 4.步幅(stride)5.三维数据的卷积运算6.结合方块思考7.批处理8.conv2d代码参考文章 1.全连接层存在的问题 在全连接层中&#xff0c;相邻层的神经元全部连接在一起&#xff0c;输出的数量可以任意决…

大数据之LibrA数据库系统概览

实时监控 “实时监控”页面如图1所示&#xff0c;用户可单击刷新按钮手动刷新当前页面&#xff0c;也可在点刷新按钮前选择自动刷新时长&#xff0c;刷新时长包括&#xff1a;每30秒刷新一次、每60秒刷新一次、停止刷新。 实时监控数据&#xff08;监控时间轴产生的新曲线&am…

win32汇编-PUSHAD和POPAD指令

PUSHAD是一个x86汇编指令&#xff0c;用于将当前程序的所有通用寄存器&#xff08;EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI&#xff09;的值依次入栈 PUSHAD指令压入32位寄存器 其堆栈指针SP将加32 PUSHAD POPAD不会影响标志位 POPAD指令则是PUSHAD指令的逆操作。POPAD指令…

家装、家居两不误,VR全景打造沉浸式家装体验

当下&#xff0c;用户对生活品质要求日益提升&#xff0c;越来越多的用户对多功能家装用品需求较大&#xff0c;由此造就了VR全景家装开始盛行。VR全景家装打破传统二维空间模式&#xff0c;通过视觉、交互等功能让用户更加真实、直观的体验和感受家居布置的效果。 一般来说&am…

数据结构-----图(graph)的储存和创建

目录 前言 图的储存结构 1.邻接矩阵 无向图的邻接矩阵 有向图的邻接矩阵 网&#xff08;赋权图&#xff09;的邻接矩阵 代码表示 2.邻接表 无向图的邻接表 有向图的邻接表 代码表示 3.邻接矩阵和邻接表对比 邻接矩阵 邻接表 图的创建 1.邻接矩阵创建图&#xff0…