首页 > 其他分享 >主动写入流对@ResponseBody注解的影响 | 京东云技术团队

主动写入流对@ResponseBody注解的影响 | 京东云技术团队

时间:2023-09-19 12:06:47浏览次数:38  
标签:map Map 代码 ResponseBody 流对 京东 response out

问题回溯

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记录如图所示:

主动写入流对@ResponseBody注解的影响 | 京东云技术团队_JSON

后续内容可以想象,肯定还有地方去把流按照指定的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源码截图

主动写入流对@ResponseBody注解的影响 | 京东云技术团队_spring_02

主动写入流对@ResponseBody注解的影响 | 京东云技术团队_spring_03

可以发现Spring对这种方式操作文件流视作异常情况,然后抛出,在后续逻辑中完成整个请求,简单来说就是 @ResponseBody 注解没起到任何作用。

因此答案呼之欲出:当时功能不可用的罪魁祸首就是相关人员没有配置参数导致,与写法没有任何关系。

结论与启发

结论:

  1. 我们要相信自己的代码,至少是要相信已经经过测试的代码。
  2. 在委托他人或者自己配置环境参数,如权限、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;
}

如此一来,当发生预期之外的情况,我们有非常明显的报错提示,当正常时又可以完美实现功能,妙哉(我觉得)~

作者:京东零售 柯贤铭

来源:京东云开发者社区 转载请注明来源

标签:map,Map,代码,ResponseBody,流对,京东,response,out
From: https://blog.51cto.com/u_15714439/7523971

相关文章

  • Stream流处理快速上手最佳实践 | 京东物流技术团队
    一引言JAVA1.8得益于Lambda所带来的函数式编程,引入了一个全新的Stream流概念Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。......
  • 慢SQL的致胜法宝 | 京东物流技术团队
    大促备战,最大的隐患项之一就是慢SQL,对于服务平稳运行带来的破坏性最大,也是日常工作中经常带来整个应用抖动的最大隐患,在日常开发中如何避免出现慢SQL,出现了慢SQL应该按照什么思路去解决是我们必须要知道的。本文主要介绍对于慢SQL的排查、解决思路,通过一个个实际的例子深入分析总结......
  • 618京东到家APP-门详页反爬实战
    一、背景与系统安全需求分析1.系统的重要性上图所示是接口所属位置、对电商平台或在线商店而言,分类查商品都是很重要的,通过为用户提供清晰的商品分类,帮助他们快速找到所需产品,节省浏览时间,提升购物效率,是购物结算产生GMV的核心环节。那么电商平台为什么都很看重商品信息的爬......
  • 京东一面:分布式 ID 生成方案怎么选?写得太好了!
    背景在分布式系统中,经常需要用到全局唯一ID发生器,标识需要存储的数据。我们需要什么样的ID生成器?ID生成器除了是数据的唯一标识以外,一般需要在系统中承担更多的责任,概括起来有以下几点:唯一性:“全局唯一”vs“业务唯一”?分布式系统使用唯一的ID生成器,会有非常严重的申请互斥......
  • 高性能MySQL实战(三):性能优化 | 京东物流技术团队
    这篇主要介绍对慢SQL优化的一些手段,而在讲解具体的优化措施之前,我想先对EXPLAIN进行介绍,它是我们在分析查询时必要的操作,理解了它输出结果的内容更有利于我们优化SQL。为了方便大家的阅读,在下文中规定类似key1的表示二级索引,key_part1表示联合索引的第一部分,unique_key1......
  • 买彩票能中大奖?用Java盘点常见的概率悖论 | 京东云技术团队
    引言《双色球头奖概率与被雷劈中的概率哪个高?》《3人轮流射击,枪法最差的反而更容易活下来?》让我们用Java来探索ta们!悖论1:著名的三门问题规则描述:你正在参加一个游戏节目,你被要求在三扇门中选择一扇:其中一扇后面有一辆车;其余两扇后面则是山羊。你选择了一道门,假设是一号门,然后......
  • SPI在Java中的实现与应用 | 京东物流技术团队
    1SPI的概念APIAPI在我们日常开发工作中是比较直观可以看到的,比如在Spring项目中,我们通常习惯在写service层代码前,添加一个接口层,对于service的调用一般也都是基于接口操作,通过依赖注入,可以使用接口实现类的实例。简单形容就是这样的:图1:API如上图所示,服务调用方无需关......
  • 高性能MySQL实战(二):索引 | 京东物流技术团队
    我们在上篇高性能MySQL实战(一):表结构中已经建立好了表结构,这篇我们则是针对已有的表结构和搜索条件为表创建索引。1.根据搜索条件创建索引我们还是先将表结构的初始化SQL拿过来:CREATETABLE`service_log`(`id`bigintUNSIGNEDNOTNULLAUTO_INCREMENTCOMMENT'主键......
  • PDF 的流对象和过滤器学习
    PDF的官方文档(https://opensource.adobe.com/dc-acrobat-sdk-docs/pdflsdk/#pdf-reference),版本为1.5,3.2.7和3.3小节一、流对象(StreamObjects)流对象(StreamObjects)和字符串对象一样,是一个字节(bytes)序列。PDF应用程序可以增量读取流,而字符串须完整读取,此外......
  • Python实现京东茅台抢购脚本, 原来这样就可以了?
    京东茅台抢购脚本可以分为以下几部分,具体实现步骤如下:登录京东账号首先需要登录京东账号。一个简单的方式是使用Python的selenium库。在使用selenium库前,需要安装selenium库和对应的浏览器驱动。示例代码如下所示:fromseleniumimportwebdriverbrowser......