1
猿与故事
程序员苏大强,平时穿着斑点衫、人字拖,若送他一盘串或一条金链子,活生生的古惑仔,所以大家都喊他强哥。不过这两天苏大强却愁眉不展,陷入了一个疑难漩涡而不能自拔。
故事的发展是这个样子的,苏大强负责开发获取银行电子凭证的需求,凭借他多年实战经验,并且拥有程序员核心技能之填坑力,自功能上线后一直平稳运行,通过监控系统来看每天成功率都是 100%,但是 6.24 这天成功率为 0%,成功率走势一下跌入谷底。
电子凭证获取成功率的走势,犹如苏大强的心跳。不过苏大强是大场面先生,啥场面都经历过,生产无小事儿,说时迟那时快,苏大强已经开启了排查问题之旅。
首先,从日志中找到 6.24 那笔获取电子凭证的请求报文,拿到如下请求下载的链接地址。
http://x.x.x.:8888/xbank/download?fileId=120_AXm+g8nbWnJ
然后,苏大强在浏览器里访问好几下,都是提示文件下载失败,开始怀疑银行没有生成电子凭证文件,遂联系行方确认电子凭证是否存在,行方确认文件着实存在。
{"result":"下载失败!","code":"999999"}
问题就是这么个问题,到底是咋回事儿呢?苏大强的心犹如小鹿在乱撞,反反复复验证了好几次,终不得其解。
正当苏大强排查验证之时,一封业务投诉的邮件映入眼帘,没想到剧情会愈演愈激烈 ... ...
当旁边的程序员王多鱼看到投诉邮件后,又看看苏大强青春痘泛滥而又愁眉不展的大宽脸,出于内心的纯真与好奇遂出手相助。
”是不是 URL 中的 + 号导致的?我之前的项目中遇到过类似的问题“王多鱼弱弱的问了一句。
”肯定是,因为其它电子凭证文件获取一切正常。“苏大强拍着大腿坚定的说。
接着,苏大强尝试对传入的文件编号的值进行编码操作。
编码前:120_AXm+g8nbWnJ
编码后:120_AXm%2Bg8nbWnJ
通过 URL 编码后的链接,再次访问银行获取电子凭证文件,果不其然,电子凭证文件正常获取。
监控系统终于恢复了往日的平静,苏大强嘴角上扬,微微一笑很倾城。
2
情景再现
尽可能的模拟复现苏大强的问题,首先模拟电子凭证下载服务端代码。
@Controller
public class DownloadController {
@RequestMapping("/download")
private void download(@RequestParam(value = "fileId") String fileId,
HttpServletResponse response) throws Exception {
System.out.println(String.format("服务端接收:要下载的文件ID【%s】", fileId));
}
}
模拟电子凭证下载客户端(尽力复原苏大强的代码)
public class Client {
public static void main(String[] args) throws Exception {
// case 1:文件id含有 + 号的问题
StringBuilder builder = new StringBuilder();
builder.append("http://localhost:8888/xbank/download");
builder.append("?fileId=").append("666_AAA+NNN");
System.out.println(String.format("客户端发送请求:下载文件的URL【%s】", builder.toString()));
HttpClientUtil.sendHttpRequestReturnStr(builder.toString());
}
}
服务端、客户端运行起来。
客户端输出
客户端发送请求:下载文件的URL【http://localhost:8888/xbank/download?fileId=666_AAA+NNN】
服务端输出
服务端接收:要下载的文件ID【666_AAA NNN】
纳尼?+ 号去哪里了?真搞丢了。
原来客户端向服务器传递参数时,URL 默认的将参数中的“+”被转义成空格导致的,尝试通过 URLEncoder 进行编码解决。
builder.append("?fileId=").append(URLEncoder.encode("666_AAA+NNN","UTF-8"));
运行客户端模拟下载凭证文件,此时服务端接收到的文件 ID 正常。
服务端接收:要下载的文件ID【666_AAA NNN】
除了本文提到的"+"会被转义,&、# 等字符也会出现解析的问题,所以能进行编码的地方最好进行编码传输,以免后顾之忧。
常使用的解决方案:
3
菜菜的闲话
《礼记·大学》有云:知止而后有定;定而后能静;静而后能安;安而后能虑;虑而后能得。
解决问题要能够让自己静下来,只有静下来方能追究问题的本质。
久经码场,能静下来写 Bug、找 Bug 可谓是一件非常幸福的事情。
一起聊技术、谈业务、喷架构,少走弯路,不踩大坑。会持续输出精彩分享,敬请期待!