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

2023-09-19 11:47:03

问题回溯

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 注解没起到任何作用。

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

结论与启发

结论:

  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;
}

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

作者:京东零售 柯贤铭

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

更多推荐

有没有免费的云渲染平台?哪家云渲染平台收费更合理?

如今,越来越多的设计师开始使用云渲染平台来加快渲染速度并降低成本。许多人都想知道是否有免费的云渲染平台,或者说哪家云渲染平台收费更合理。在本文中,小编将详细介绍这些问题,帮助您更好地了解和选择适合您需求的云渲染平台。一、有免费的云渲染平台吗?首先,目前市面上没有完全免费的云渲染平台。这是因为云渲染平台建设和维护都需要资

Learn Prompt-ChatGPT 精选案例:广告文案

ChatGPT可以帮助我们生成广告文案和宣传图片,这对营销品牌建设很有帮助。通常,一个产品会有一个主要的广告词,传达设计理念或宣传产品的好处。我们可以尝试直接生成文案,看看ChatGPT有没有好的创意。假设我们的产品是一款登山鞋,我们要怎么得到令人满意的文案和宣传图片呢?背景查询​ChatGPT能帮助你快速熟悉一个领域

分享一个基于微信小程序的社区生活小助手源码调试和lw,有java+python双版本

💕💕作者:计算机源码社💕💕个人简介:本人七年开发经验,擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等,大家有这一块的问题可以一起交流!💕💕学习资料、程序开发、技术解答、文档报告💕💕如需要源码,可以扫取文章下方二维码联系咨询💕💕JavaWeb项目💕💕微信小程序项目💕💕

C++ STL & 标准库

STLSTL(标准模板库)是一套C++模板类,提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。C++STL的核心包括三个组件:容器(Containers)用来管理某一类对象的集合。C++提供了各种不同类型的容器,比如deque、list、vector、map等。

Python自动化小技巧23——PDF文件拆分为单独页面(PyMuPDF)

其实编辑PDF用Adobe就行,它功能超级齐全,可是这玩意要收费...去弄免费破解版,找资源又得半天,所以用python来拆分PDF文件吧,可以批量化处理。至于为什么不用WPS.....别问,问就是不想开会员。脚本代码先安装PyMuPDF库,pipinstallPyMuPDF代码:importfitz#PyMuPDFd

AdsPower RPA一些编写思路(进阶)

在使用AdsPowerRPA编写RPA流程(有的人也叫RPA模板,都是一个意思)之前,我们需要慢慢梳理整个业务流程。而不是一上来就开写,要不然可能会来来回回改很多次,甚至会推倒之前写的步骤,很伤脑筋。今天就让Tool哥来给大家介绍一下我平常写RPA流程的思路,希望能帮助到大家。本文讲的内容稍微有些深入。如果是刚接触RP

Linux 企业级夜莺监控分析工具远程访问

文章目录前言1.Linux部署Nightingale2.本地访问测试3.Linux安装cpolar4.配置Nightingale公网访问地址5.公网远程访问Nightingale管理界面6.固定Nightingale公网地址前言夜莺监控是一款开源云原生观测分析工具,采用All-in-One的设计理念,集数据采集、可视化

缺口的大利润!伦敦银如何使用缺口交易

在伦敦银市场中,我们经常能够看见市场跳空形成缺口,其实,如果利用得当,我们在伦敦银投之中,这些缺口是能够为我们创造盈利机会的,那么下面我们就来讨论一下在伦敦银投之中如何认识这些跳空缺口,并且利用这些缺口为我们提供一些高概率的交易机会。在伦敦银市场中,所谓的缺口(Gap),指的是今天的开盘价高于昨天的收盘价,中间并没有任

210. 课程表 II

210.课程表II题目-中等难度示例1.bfs题目-中等难度现在你总共有numCourses门课需要选,记为0到numCourses-1。给你一个数组prerequisites,其中prerequisites[i]=[ai,bi],表示在选修课程ai前必须先选修bi。例如,想要学习课程0,你需要先完成课程1,我们用一个

工作中怎么去进行测试用例的编写

作为一个测试人员,无论是测试资深大佬还是刚入门的测试小白应该都知道,编写测试用例是我们测试的核心工作之一,往往测试用例写的标准与否,最能体现我们测试人员的差距,那么如何编写一篇优秀高质量的测试用例呢?首先我们要想编写一份符合需求的高质量的测试用例的话,我们最重要的步骤就是要先分析自己的需求,只有把需求分析透彻了,才能写

全国职业技能大赛云计算--高职组赛题卷③(容器云)

全国职业技能大赛云计算--高职组赛题卷③(私有云)第二场次题目:容器云平台部署与运维任务1DockerCE及私有仓库安装任务(5分)任务2基于容器的web应用系统部署任务(15分)任务3基于容器的持续集成部署任务(15分)任务4Kubernetes容器云平台部署与运维(15分,本任务只公布考试范围,不公布赛题)需要环境

热文推荐