如何拦截响应内容并修改响应头
背景及需求描述
背景
记录分享下近期遇到并解决的困扰了比较久的问题:在不同系统微信生态发现同一个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请求就可以对请求和响应进行拦截,并做出相应的修改。下面我们拆解下任务:
- 实现前端发送一个http请求并下载资源;
- 拦截并改写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)

完结撒花,如有谬误,敬请指出~
关注我,一起学习前端知识【后面会介绍同时下载大量级文件、大文件如何处理,敬请期待~】

相关文章:
如何拦截响应内容并修改响应头
背景及需求描述 背景 记录分享下近期遇到并解决的困扰了比较久的问题:在不同系统微信生态发现同一个cos地址用window.open(url)打开在苹果和安卓设备的微信生态上表现不一致:对于文档类型,响应头Content-Type: application/pdf 在安卓微信上…...
分类预测 | Matlab实现WOA-GRU鲸鱼算法优化门控循环单元的数据多输入分类预测
分类预测 | Matlab实现WOA-GRU鲸鱼算法优化门控循环单元的数据多输入分类预测 目录 分类预测 | Matlab实现WOA-GRU鲸鱼算法优化门控循环单元的数据多输入分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现WOA-GRU鲸鱼算法优化门控循环单元的数据多输入…...
特定深度节点链表
题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 经典BFS与简单链表结合的题目。 #define MAX_DEPTH (1000)struct ListNode** listOfDepth(struct TreeNode* tree, int* returnSize) {*returnSize 0;struct ListNode **ans (…...
【css】背景换颜色
更换前 longin.html <!DOCTYPE html> <html lang"en" > <head><meta charset"UTF-8"><title>login</title><link href"/css/style.css" type"text/css" rel"stylesheet"><s…...
什么是美颜sdk?直播实时美颜sdk的工作流程和架构分析
在现代社交媒体和娱乐行业中,直播已经成为了一种受欢迎的娱乐形式,同时实时美颜也变得越来越重要。直播实时美颜SDK的工作流程和架构在这一领域起到了关键作用。本文将深入探讨这些SDK的内部机制,从而理解它们如何为用户提供出色的美颜效果。…...
第二证券:跌破3000点,热搜第一!
今天上午,“沪指开盘跌破3000点关口”冲上百度热搜榜榜首。 上午收盘,上证指数下跌0.27%,报2997.22点;深证成指下跌0.36%,创业板指下跌0.44%。 赛道股发力,光伏、风电、新能源轿车等板块盘中冲高。房地产…...
IJCAI2023【基于双曲空间探索的非独立同分布联邦学习】
1、介绍汇报的主题及汇报者 2、粗略介绍面临的挑战及出发点 3、介绍一下预备知识 4、解决方案 5、总览 6、实验设置 7、实验 8、结论...
实现Linux下Word转PDF、Java调用命令方式
使用 LibreOffice 实现 Word 转 PDF 和 Java 调用命令 1、 安装 LibreOffice 外网安装 # 一键安装 yum install -y libreoffice # 验证版本 libreoffice --version # Warning: -version is deprecated. Use --version instead. # LibreOffice 7.5.6.2 f654817fb68d6d4600d7…...
Java并发-06-AQS(AbstractQueuedSynchronizer)相关
1-概述 AQS全称是 AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具的框架。同步器的设计是基于模板方法模式的,也就是说,使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并…...
【Python接口自动化】--深入了解HTTP接口基本组成和网页构建原理
引言 Python接口自动化有着广泛的应用场景,但是在实际使用过程中,可能会出现一些问题。比如,你不知道HTTP接口的基本构成,也不清楚网页是如何构建的。 这时,你就需要深入了解HTTP接口的基本组成和网页构建原理。通过本…...
window mysql5.7.27 启用SSL openssl mysql_ssl_rsa_setup
应客户监管部门要求 mysql必须要启用SSL。由于mysql安装在window上,启用过程中遇到了不少的坑,在此记录一下。 安装openssl 如果已经安装过可跳过此步 https://slproweb.com/download/Win64OpenSSL-1_1_1w.msi复制到浏览器下载后安装即可。如果需要其他…...
性能测试-JMeter分布式测试及其详细步骤
性能测试概要 性能测试是软件测试中的一种,它可以衡量系统的稳定性、扩展性、可靠性、速度和资源使用。它可以发现性能瓶颈,确保能满足业务需求。很多系统都需要做性能测试,如Web应用、数据库和操作系统等。 性能测试种类非常多,…...
学习gin-vue-admin之创建api和swagger
文章目录 go:generateViper 读写配置文件ZAP 保存日志定时任务创建apimodel步骤 1. 创建service步骤 2. 创建api步骤 3. 创建router 初始化总路由启动go-swagger路由配置swag init test将嵌套结构定义为指针或对象利弊结构体嵌套学习资源 go:generate //go:generate go env -w …...
2023-10-17 mysql-innodb-解析write_row的record的一行数据-分析
摘要: 2023-10-17 mysql-innodb-解析write_row的record的一行数据-分析. record是一行数据的序列化后的一整个字节流, 在innodb中需要解读出字段. 本文分析如何解析record, 以便学习这种技巧. row_mysql_store_col_in_innobase_format 调用堆栈: #0 row_mysql_store_col_in…...
认识web自动化测试!
1.什么是自动化测试? 自动化测试的概念: 软件自动化测试就是通过测试工具或者其他手段,按照测试人员的预定计划对软件产品进行自动化测试,他是软件测试的一个重要组成部分,能够完成许多手工测试无法完成或者难以实现的测试工作&a…...
多商户进驻小程序商城的作用是什么
多商户进驻商城简单来说就是在一个商城里,由经营者邀请同行、异业商家进驻到商城里(子商户),可丰富商城经营业态,满足客户多方购物需求,打造购物商圈及经营者获得更多收益等。 通过【雨科】平台的多商户进驻…...
接口响应慢该如何排查
不知道大家有没有遇到这种情况,接口业务逻辑写完后,用 postman 一调,发现接口响应时间好长,不得不对接口进行优化。但是此时接口的代码往往逻辑比较复杂,调用层次也比较多,很难定位到耗时较长的代码块。 遇…...
spring boot MongoDB实战
文章目录 项目搭建文章评论实体类的编写文章评论的基本增删改查根据上级ID查询文章评论的分页列表MongoTemplate实现评论点赞 GITHUB 项目搭建 <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0&q…...
企业数字化转型时,会遇到的5大挑战
企业数字化转型时,会遇到的5大挑战添加链接描述 数字化转型已然是当今商业战略的一大基石,根据Gartner的《2023年度董事会调查》显示,有89%的企业将数字业务视为其增长的核心。但该研究的另一项统计数据也显示:在这些企业中&…...
动态语句 sqlserver
EXEC sp_executesql DynamicSQL, NFirstName NVARCHAR(50), LastName NVARCHAR(50), FirstName, LastName在EXEC sp_executesql语句中,后面的参数需要按特定顺序传递。这些参数的顺序如下: 1.第一个参数是动态SQL语句本身,通常是一个NVARCHA…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
什么是Ansible Jinja2
理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具,可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板,允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板,并通…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...
智能职业发展系统:AI驱动的职业规划平台技术解析
智能职业发展系统:AI驱动的职业规划平台技术解析 引言:数字时代的职业革命 在当今瞬息万变的就业市场中,传统的职业规划方法已无法满足个人和企业的需求。据统计,全球每年有超过2亿人面临职业转型困境,而企业也因此遭…...
NineData数据库DevOps功能全面支持百度智能云向量数据库 VectorDB,助力企业 AI 应用高效落地
NineData 的数据库 DevOps 解决方案已完成对百度智能云向量数据库 VectorDB 的全链路适配,成为国内首批提供 VectorDB 原生操作能力的服务商。此次合作聚焦 AI 开发核心场景,通过标准化 SQL 工作台与细粒度权限管控两大能力,助力企业安全高效…...
数据库优化实战指南:提升性能的黄金法则
在现代软件系统中,数据库性能直接影响应用的响应速度和用户体验。面对数据量激增、访问压力增大,数据库性能瓶颈经常成为项目痛点。如何科学有效地优化数据库,提升查询效率和系统稳定性,是每位开发与运维人员必备的技能。 本文结…...
mq安装新版-3.13.7的安装
一、下载包,上传到服务器 https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.13.7/rabbitmq-server-generic-unix-3.13.7.tar.xz 二、 erlang直接安装 rpm -ivh erlang-26.2.4-1.el8.x86_64.rpm不需要配置环境变量,直接就安装了。 erl…...
20250607在荣品的PRO-RK3566开发板的Android13系统下实现长按开机之后出现插入适配器不会自动启动的问题的解决
20250607在荣品的PRO-RK3566开发板的Android13系统下实现长按开机之后出现插入适配器不会自动启动的问题的解决 2025/6/7 17:20 缘起: 1、根据RK809的DATASHEET,短按开机【100ms/500ms】/长按关机,长按关机。6s/8s/10s 我在网上找到的DATASHE…...
