主动写入流对@ResponseBody注解的影响 | 京东云技术团队
问题回溯
2023年Q2某日运营反馈一个问题,商品系统商家中心某批量工具模板无法下载,导致功能无法使用(因为模板是动态变化的)
商家中心报错(JSON串):
{"code":-1,"msg":"失败"}
负责的同事看到失败后立即与我展开讨论(因为不是关键业务,所以不需要回滚,修复即可),我们发现新功能模板下载的代码与之前的代码有所不同,恰好之前的功能又可以正常运行,所以同事对现有代码进行改造然后预发布测试完成后再次上线。
其他业务代码:
/** * 模板下载 */ @RequestMapping("/doBatchWareSetAd") public void doBatchWareSetAd(@RequestParam MultipartFile file, HttpServletResponse response) { wareBatchBusiness.doBatchWareSetAd(file, response, getLongOrgCode(), getCurrentUserPin(), getCurrentUserId()); }
问题业务代码:
/** * 模板下载 */ @RequestMapping("/doBatchWareSetAdDemo") @ResponseBody public Map<String, Object> doBatchWareSetAd(@RequestParam MultipartFile file, HttpServletResponse response) { return wareBatchBusiness.doBatchWareSetAd(file, response, getLongOrgCode(), getCurrentUserPin(), getCurrentUserId()); }
上线的结果是;仍然无法使用。
其实也正常:因为两种代码在预发布都可以正常运行,在线上出错只可能是因为其他原因,只不过我们不了解底层原理,害怕它 "可能" 有问题罢了,最终查询得到的结论是权限系统管理员在线上环境没有给我们配置相应的文件,导致请求为空,导致请求失败。
探索 @ResponseBody 与主动写入流的关系
我们都知道 @ResponseBody
注解可以帮助我们把返回对象转化为JSON,方便展示和交互。
那它到底是如何工作的呢,请看下面的讲解:
代码案例1:
@RequestMapping("/test1") @ResponseBody public Map<String, String> test1(HttpServletResponse response) { Map<String, String> map = new HashMap<>(); map.put("1", "1"); return map; } // 响应 JSON报文
跟代码发现其核心处理类为:RequestResponseBodyMethodProcessor.java
方法:org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue 会处理其相关返回值。
真正的核心处理方法:org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor#writeWithMessageConverters
关键DEBUG记录
如图所示:
后续内容可以想象,肯定还有地方去把流按照指定的HEADER
写入,因为和本文无关所以不深究。
再来看代码案例2:
@RequestMapping("/test2") @ResponseBody public Map<String, String> test2(HttpServletResponse response) throws IOException { Map<String, String> map = new HashMap<>(); map.put("1", "1"); response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", String.format( "attachment; filename=%s_%s.xls", "Demo", System.currentTimeMillis())); OutputStream out = response.getOutputStream(); out.flush(); out.close(); return map; } // 响应 提示下载文件
关键DEBUG源码截图
:
可以发现Spring对这种方式操作文件流视作异常情况,然后抛出,在后续逻辑中完成整个请求,简单来说就是 @ResponseBody
注解没起到任何作用。
因此答案呼之欲出:当时功能不可用的罪魁祸首就是相关人员没有配置参数导致,与写法没有任何关系。
结论与启发
结论:
- 我们要相信自己的代码,至少是要相信已经经过测试的代码。
- 在委托他人或者自己配置环境参数,如权限、ZK等每次都保证预发布和线上同时配置,避免遗漏的情况。
启发:
聊了这么多,那我们这种类似场景的代码应该怎么写?
既然主动写入流会解除@ResponseBody的作用,反之又能发挥它的作用,那我们最佳方案是不是如下所示?
@RequestMapping("/test1") @ResponseBody public Map<String, String> test1(HttpServletResponse response) { Map<String, String> map = new HashMap(); if (获取不到文件配置 == true) { return map.put("msg", "获取不到文件配置"); } response.setContentType("application/vnd.ms-excel"); response.setHeader("Content-Disposition", String.format( "attachment; filename=%s_%s.xls", "Demo", System.currentTimeMillis())); OutputStream out = response.getOutputStream(); out.flush(); out.close(); return map; }
如此一来,当发生预期之外的情况,我们有非常明显的报错提示,当正常时又可以完美实现功能,妙哉(我觉得)~
作者:京东零售 柯贤铭
来源:京东云开发者社区 转载请注明来源
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
慢SQL治理实践及落地成果分享 | 京东物流技术团队
一、治理背景 数据库系统性能问题会对应用程序的性能和用户体验产生负面影响。慢查询可能导致应用程序响应变慢、请求堆积、系统负载增加等问题,甚至引发系统崩溃或不可用的情况。慢SQL治理是在数据库系统中针对执行缓慢的SQL查询进行优化和改进的一项重要工作。 但原有的治理节奏,一般在大促备战期间,集中投入人力紧急治理,日常对慢SQL的关注度不高;即使研发团队想着手治理,实例下的SQL明细筛选繁琐,趋势不明,缺少系统化,数字化的治理方案。 所以为了保证系统稳定性,预防潜在慢SQL导致应急事故,发起慢SQL常态化备战专项,下文主要描述专项的实践及落地情况。 二、阶段规划 1.0阶段 目标:【形成常态化治理机制,关注慢SQL解决的有效率】 改变慢SQL治理习惯,由原大促备战期间治理,落地按照日维度产生的慢SQL每天跟进,关注双周维度治理的有效率。 关注指标: 逾期率 = 工单逾期数量(创建时间在当季度的任务)/总量(创建时间在当季度的任务) 注:超过14天未处理完成的算逾期,逾期与否以第一次完成的时间来判断,如果在截止日期前未完成,算逾期;如果在截止日期前完成,但是重开后,在截止日期后完成,不算逾期...
- 下一篇
腾讯云微服务平台 TSF 异地多活单元化能力重磅升级
导语 2023腾讯全球数字生态大会已于9月7-8日完美落幕,40+专场活动展示了腾讯最新的前沿技术、核心产品、解决方案。 微服务与消息队列专场,腾讯云微服务平台 TSF 产品经理张桢带来了《腾讯云微服务平台 TSF 异地多活单元化能力重磅升级》的精彩演讲。本篇文章详细回顾了腾讯云微服务单元化最佳实践。 单元化架构的概述 什么是单元化 从目前的服务化架构看起,传统的架构下服务是分层的,每一层使用不同的分区算法,每一层都有不同数量的节点,上层节点随机选择下层节点。这种不确定性,就会导致上层节点访问下层节点时有可能跨区或者跨地域。而跨区跨地域的调用代价是很高的,不仅要解决时延的问题,还要保证数据同步,这两点在技术实现上都具有很大的挑战性。 那换一个思路,事先设计好调用的路径,让请求沿着规划好的路径进行调用,这样的设计路径就可以解决以上的挑战。单元化架构的出现,就是遵循这样的设计,在单元化架构下,接入层、服务层、数据层使用相同的分区算法,实现计算资源与数据资源进行逻辑上的绑定,最终形成一个个标准化的处理单元。 单元的特征与类型 了解了什么是单元化,接下来再看下单元的特征与类型。 单元的特征 1...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- 设置Eclipse缩进为4个空格,增强代码规范
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- Hadoop3单机部署,实现最简伪集群