1、問題描述
最近在做有關大數據的項目中,解碼客戶端的請求參數時拋出異常:
java.lang.IllegalArgumentException: URLDecoder: Incomplete trailing escape (%) pattern
詳細錯誤如下:
java.lang.IllegalArgumentException: URLDecoder: Incomplete trailing escape (%) pattern at java.net.URLDecoder.decode(URLDecoder.java:187) at com.z.transformer.util.LoggerUtil.handleRequestBody(LoggerUtil.java:122) at com.z.transformer.util.LoggerUtil.handleLogText(LoggerUtil.java:59) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:47) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:20) at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:146) at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:787) at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341) at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:164) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1657) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158) 2019-04-16 18:49:28,087 WARN [main] com.z.transformer.util.LoggerUtil: 解碼失敗:tt=%E6%A8%AA%E5%BA%97%E5%A5%BD%E5%9C%A8%E5%93%AA%E9%87%8C%EF%BC%9F%E4%B8%BA%E4%BB%80%E4%B9%88%E5%BE%88%E5%A4%9A%E6%98%8E%E6%98%9F%E9%83%BD%E5%8E%BB%E6%A8%AA%E5%BA%97%E6%8B%8D%E6%88%8F_%E9%A9%B4%E5%A6%88%E5%A6%88%E6%97%85%E6%B8%B8%E8%B5%84% java.lang.IllegalArgumentException: URLDecoder: Incomplete trailing escape (%) pattern at java.net.URLDecoder.decode(URLDecoder.java:187) at com.z.transformer.util.LoggerUtil.handleRequestBody(LoggerUtil.java:122) at com.z.transformer.util.LoggerUtil.handleLogText(LoggerUtil.java:59) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:47) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:20) at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:146) at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:787) at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341) at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:164) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1657) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158) 2019-04-16 18:49:31,067 WARN [main] com.z.transformer.util.LoggerUtil: 解碼失敗:p_url=http%3A%2F%2Fwww.baidu.com%2Finfo%2 java.lang.IllegalArgumentException: URLDecoder: Incomplete trailing escape (%) pattern at java.net.URLDecoder.decode(URLDecoder.java:187) at com.z.transformer.util.LoggerUtil.handleRequestBody(LoggerUtil.java:122) at com.z.transformer.util.LoggerUtil.handleLogText(LoggerUtil.java:59) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:47) at com.z.transformer.mr.etl.AnalysisDataMapper.map(AnalysisDataMapper.java:20) at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:146) at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:787) at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341) at org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:164) at java.security.AccessController.doPrivileged(Native Method) at javax.security.auth.Subject.doAs(Subject.java:422) at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1657) at org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:158)
截圖如下:
我的主要代碼如下:(代碼加重部分)
/** * 處理請求參數 * 處理結果保存到參數 result 集合(Map 集合) * * @param clientInfo * 保存最終用戶行為數據的 map 集合 * @param requestBody * 請求參數中,用戶行為數據,格式為: * u_nu=1&u_sd=6D4F89C0-E17B-45D0-BFE0-059644C1878D&c_time= * 1450569596991&ver=1&en=e_l&pl=website&sdk=js&b_rst=1440*900& * u_ud=4B16B8BB-D6AA-4118-87F8-C58680D22657&b_iev=Mozilla%2F5.0% * 20(Windows%20NT%205.1)%20AppleWebKit%2F537.36%20(KHTML%2C% * 20like%20Gecko)%20Chrome%2F45.0.2454.101%20Safari%2F537.36&l= * zh-CN&bf_sid=33cbf257-3b11-4abd-ac70-c5fc47afb797_11177014 */ private static void handleRequestBody(Map<String, String> clientInfo, String requestBody) { // 將請求參數體按照 & 切割 String[] parameters = requestBody.split("&"); for (String parameter : parameters) { // 循環處理參數,parameter 格式為: c_time=1450569596991 = 只會出現一次 String[] params = parameter.split("="); String key, value = null; try { // 使用 utf-8 解碼 key = URLDecoder.decode(params[0].trim(), "UTF-8"); value = URLDecoder.decode(params[1].trim(), "UTF-8"); // 添加到結果集合 Map 中 clientInfo.put(key, value); } catch (Exception e) { logger.warn("解碼失敗:" + parameter, e); } } }
出現這個錯誤的主要原因是,在接收字段 params[0].trim() 的值時,瀏覽器傳過來的值有時候是如下這樣的:
b_iev=Mozilla%2F5.0%20(Windows%20NT%205.1)%20AppleWebKit%2F537.36%20(KHTML%2C%20like%20Gecko)%20Chrome%2F45.0.2454.101%20Safari%2F537.36
主要原因是% 在URL中是特殊字符,需要特殊轉義一下。
2、解決辦法
我們只需要將傳入后台的參數字符在decode之前使用 replaceAll("%","%25") 一下即可:
這里強調下,網上很多的改法如下:
上面這種寫法是錯誤的,因為其把正確的都替換掉了。
這里我們需要明白,要替換掉的是單獨出現的百分號,而不是全部的百分號。replaceAll("%(?![0-9a-fA-F]{2})","%25")
正確改法:
講解下 %(?![0-9a-fA-F]{2})
: 這是個正則表達式,含義是:不匹配 %后面兩位為
數字或字母(包括大小寫)的字符;這樣就把正確的排除掉了,剩下的就是需要匹配替換的。
3、效果