如何拦截响应内容并修改响应头
背景及需求描述
背景
记录分享下近期遇到并解决的困扰了比较久的问题:在不同系统微信生态发现同一个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…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

shell脚本--常见案例
1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件: 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...