首页 > 其他分享 >主动写入流对@ResponseBody注解的影响

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

时间:2024-10-29 11:34:40浏览次数:1  
标签: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记录如图所示:

 


 

 

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

 

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

标签:map,Map,代码,写入,ResponseBody,流对,response,out
From: https://www.cnblogs.com/Jcloud/p/18512619

相关文章

  • 【单片机】用mipi_dsi点亮屏幕,帧缓存写入失败原因
    mipi_dsi协议    mipi协议的优点在于传输快,可以通过双边传输的,然后可以多组通讯线进行数据传输,也就是所谓的lane,正常都是1组或者2组。其快速通讯和处理的功能主要用于显示界面UI的设计,也就是所谓的LVGL。      但是其缺点也很明显,就在于没有内部内存......
  • 数学建模例题2.38 数据写入文件示例
    2.38.1`importpandasaspdimportnumpyasnpdates=pd.date_range(start='20191101',end='20191124',freq='D')a1=pd.DataFrame(np.random.randn(24,4),index=dates,columns=list('ABCD'))a2=pd.DataFrame(np.random.rand......
  • C++/CLI使用Office.Interop库创建excel,同时解决写入速度慢的问题
    boolWriteExcelFile_OfficeInterop(String^path,DataSet^dt, conststd::vector<std::string>&sheetName,boolhideColumnName) { //Ifthefilealreadyexists,deleteitandthengeneratefile if(System::IO::File::Exists(path)) { try......
  • PbootCMS网站提示:”会话目录写入权限不足“
    当您遇到“会话目录写入权限不足”的提示时,通常是因为服务器上的某些目录缺少写入权限。根据您的描述,需要对根目录下的config、data和runtime目录进行权限设置。以下是详细的解决步骤:解决步骤检查文件夹权限:确保根目录下的config、data和runtime文件夹具有适当的写入权限。......
  • 字节流写入文件
    一、创建输出流对象表示的文件三种方式方法一:FileOutputStreamfos=newFileOutputStream("fos.txt",true);//最简便方法二:FileOutputStreamfos=newFileOutputStream(newFile("fos.txt"));方法三;Filef=newFile("fos.txt");FileOutputStreamfos=new......
  • ELK04 ELK综合案例, logstash写入mysql, kibana访问验证 ubuntu使用
    6ELK综合实战案例6.1Filebeat收集Nginx日志利用Redis缓存发送至Elasticsearch 图上ip地址仅供参考6.1.2.2修改Filebeat配置#安装redis(访问0.0.0.0和密码123456),nginx(访问日志json格式)[root@ubuntu~]#vim/etc/filebeat/filebeat.ymlfilebeat.inputs:-......
  • 大数据-180 Elasticsearch - 原理剖析 索引写入与近实时搜索
    点一下关注吧!!!非常感谢!!持续更新!!!目前已经更新到了:Hadoop(已更完)HDFS(已更完)MapReduce(已更完)Hive(已更完)Flume(已更完)Sqoop(已更完)Zookeeper(已更完)HBase(已更完)Redis(已更完)Kafka(已更完)Spark(已更完)Flink(已更完)ClickHouse(已更完)Kudu(已更完)Druid(已更完)Kylin(已更完)Elasticsearch(正在更......
  • pbootcms设置的会话目录创建失败!runtime/session/无法写入的解决方案
    当用户在安装PBootCMS模板时遇到报错信息:“pbootcms设置的会话目录创建失败!网站目录/runtime/session/无法接入”,可以尝试以下两种解决方案:解决方案一:检查网站目录权限登录服务器:通过SSH登录到你的服务器。更改目录权限:使用 chmod 命令更改 runtime/ 目录及其子目......
  • PbootCMS网站提示:“会话目录写入权限不足”的解决办法
    针对PbootCMS网站提示“会话目录写入权限不足”的问题,可以按照以下步骤进行解决:解决步骤确定需要修改权限的目录:config 目录(存放授权码与数据库配置文件)data 目录(存放SQLite数据库文件)runtime 目录(存放日志文件)修改目录权限:使用FTP工具或SSH连接到服务器,根据实际......
  • PBOOTCMS登录请求发生错误,您可按照如下方式排查: 1、试着删除根目录下runtime目录,刷新
    当您在使用PbootCMS时,后台登录请求发生错误,提示“表单提交校验失败,请刷新后重试”。这通常是由于缓存文件过多、会话文件存储目录权限问题或服务器环境问题引起的。解决方法删除 runtime 目录步骤:备份文件:在进行任何修改前,请先备份 runtime 文件夹,以防止意外情况......