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

【一文清晰】单元测试到底是什么?应该怎么做?
我是java程序员出身,后来因为工作原因转到到了测试开发岗位。测试开发工作很多年后,现在是一名自由职业者 1、什么是单元测试 2、该怎么做单元测试 一、什么是单元测试? 单元测试(unit testing),是指对软件…...

二、基于PCL的RANSAC拟合点云中所有直线或平面——3D点云处理系列
RANSAC原理:略。 其他博客大多都是介绍拟合单条直线或平面的代码案例,本文介绍如何拟合多条直线或平面,其实是在单个拟合的基础上接着拟合,以此类推。 注意:步骤中的直线模型是每次随机在点云中取点计算的。 步骤&…...

Linux实用指令-指定运行级别、帮助指令
一、 指定运行级别 1.运行级别说明: 0:关机 1:单用户[找回丢失密码] 2:多用户状态没有网络服务 3:多用户状态有网络服务 4:系统未使用保留给用户 5:图形界面 6:系统重启 常用运行级别是3和5,要修改默认的运行级别。可改文件/etc/inittab 的id:5:initd…...

【LeetCode】2562. 找出数组的串联值
难度:简单 题目 给你一个下标从 0 开始的整数数组 nums 。 现定义两个数字的 串联 是由这两个数值串联起来形成的新数字。 例如,15 和 49 的串联是 1549 。 nums 的 串联值 最初等于 0 。执行下述操作直到 nums 变为空: 如果 nums 中存…...

Hive知识梳理(好文)
Hive是建立在 Hadoop 上的数据仓库基础构架。可以将SQL查询转换为MapReduce的job在Hadoop集群上执行。 元数据 Hive元数据信息存储在Hive MetaStore中,或者mysql中。 分隔符 Hive默认的分格符有三种,分别是(Ctrl/A)、࿰…...

GitHub仓库的README文件无法显示图片问题-非域名污染原因
之前上自己仓库就偶然发现图片不显示现象,当时以为是网络问题就没有留意这事。但是一直不显示就有问题了!于是网上搜了一遭,看见大家遇到此现象的原因普遍归于DNS污染1而我的问题原来是MarkDown格式! 在图片语法前不要加分区语法…...

opencv入门到精通——图片,视频,摄像头的读取与保存
简介 OpenCV是一个流行的开源计算机视觉库,由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包,可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和计算机视觉应用。OpenCV主要使用C语言编写,同时也支持Python、Java、C等…...

Android 13.0 开机动画支持mp4格式视频作为开机动画播放
1.概述 在13.0的系统产品开发中,在系统开机动画这块一般情况下都是播放开机图片,然后绘制多张开机图片形成开机动画模式,而产品需求要求支持开机mp4格式的短视频来作为开机动画播放视频来介绍产品情况,就需要用开机视频来替代开机动画来实现功能 2.开机动画支持mp4格式视频…...

前端自学需要把大量时间放在 HTML、CSS 吗?
前言 html和css其实不需要花费太多的时间,html暂且不说,css各类的属性太多了,平时如果只是简单做一些小网站根本不需要全部掌握,只需要掌握一些基础常用的即可,一般遇到不会的也可以直接查文档,就我个人来…...

Python爬虫如何设置代理服务器(搭建代理服务器教程)
在Python爬虫中使用代理服务器可以提高爬取数据的效率和稳定性。本文将为您提供搭建代理服务器的详细教程,并提供示例代码,帮助您在Python爬虫中设置代理服务器,实现更高效、稳定的数据抓取。 Python爬虫怎么设置代理服务器(搭建代…...