百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

主动写入流对@ResponseBody注解的影响

yuyutoo 2024-12-26 17:35 2 浏览 0 评论

作者:京东零售 柯贤铭

问题回溯

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


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

相关推荐

了解一点ESB 了解一点点的意思的成语

如何进行系统集成?点对点方式VS数据总线方式企业服务总线,即ESB全称为EnterpriseServiceBus如上图所示,企业服务总线是将多个系统(一般是公司内部的多个系统)进行集成,避免服...

基础回顾Servlet系列:request,response,ServletContext

Servlet系列:(HttpServletRequest、HttpServletResponse、ServletContext、ServletConfig)详解HttpServletRequestH...

主动写入流对@ResponseBody注解的影响

作者:京东零售柯贤铭问题回溯2023年Q2某日运营反馈一个问题,商品系统商家中心某批量工具模板无法下载,导致功能无法使用(因为模板是动态变化的)商家中心报错(JSON串):...

setCharacterEncoding和setContentType的区别

setCharacterEncoding:只是设置字符的编码方式response.setCharacterEncoding("utf-8");...

重定向与转发 重定向和转发的区别及应用

请求转发(forward):发送一次请求,将表单数据或封装到url中的数据一并转发到新页面。...

多人同时导出 Excel 干崩服务器!参考阿里大佬给出的解决方案

前言业务诉求:考虑到数据库数据日渐增多,导出会有全量数据的导出,多人同时导出可以会对服务性能造成影响,导出涉及到mysql查询的io操作,还涉及文件输入、输出流的io操作,所以对服务器的性能会影响的比...

一篇文章弄懂Request和Response(建议收藏复习)

一:HttpServletRequest1.简介:HttpServletRequest是专用于HTTP协议的ServletRequest子接口,它用于封装HTTP请求消息。它在每次请求serv...

谷歌Chrome 130稳定版登场:改进文档画中画、增强CSS嵌套声明

IT之家10月16日消息,谷歌公司今天(10月16日)发布新闻稿,面向安卓、ChromeOS、Linux、macOS和Windows系统,正式推出Chrome130稳定版浏览...

Google发布新版Gmail API gmail update apk

今天的I/O大会,Google发布了新版的GmailAPI(Beta),相比以往的IMAP,新API的最大优势就是资源获取速度的提高。通过以往的IMAP,第三方App每执行一次操作都需要全盘调用用...

谷歌开放语音识别 API,与 Nuance 展开正面较量

谷歌今天向第三方开发者开放了语音识别API,计划与Nuance和其他语音识别公司展开正面竞争。为了吸引开发者,GoogleCloudSpeechAPI一开始将免费提供,以后再进行收费。过...

谷歌Gemini 2.0发布,引入代理AI 谷歌mini diva

谷歌宣布对AI模型Gemini进行重大更新,发布“2.0”更新。更新后的AI模型具有更广泛的多模式推理,并在其软件包中引入了代理AI。Gemini2.0Flash是Gemini2.0完整套件的低...

Google这款工具再次限免,需要的速度了。再次错过就可惜了

Google将恢复对GoogleTranslate网站翻译器小部件的支持,并将其免费提供给非商业用途。  Google再次支持GoogleTranslate网站翻译工具,以帮助人们在COVID-1...

谷歌地图API的三大开源替代品 谷歌地图开发

CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面。如果您想投稿、寻求《近匠》报道,或给文章挑错,欢迎发送...

Go 官宣:新版 Protobuf API protobuffer golang

原文作者:JoeTsai,DamienNeil和HerbieOng原文链接:https://blog.golang.org/a-new-go-api-for-protocol-buffer...

全自动翻译国际化(支持一键翻译多国语言,不入侵业务代码)

前言Hi~大家好,今天给大家介绍一个关于国际化的vite插件vite-plugin-auto-i18n,一个自动翻译的关于i18n的vite插件。做过国际化的朋友都知道,国际化通常都是用i18n...

取消回复欢迎 发表评论: