tomcat与jetty接收请求参数的区别
【场景】
服务端点对点通知。
A服务发起请求B服务,B同步返回接收成功;然后B开始处理逻辑;B处理完成后异步通知给A;A接收请求并处理,同步回写响应给B;完成。
【先上代码】
服务端(接收端)代码:
import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.Map; public class ServController extends HttpServlet { @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("==============================="); try { Map<String, String[]> mapByRequest = request.getParameterMap(); // Map mapByRequest = ReadHttpRequest.getRequestMap(request); //request.getParameterMap(); System.out.println("map count:" + mapByRequest.size()); if(mapByRequest!=null && mapByRequest.size()>0) { System.out.println("map[0]:" + mapByRequest.keySet().toArray()[0]); } } catch (Exception e) { e.printStackTrace(); } // String requestString = ReadHttpRequest.getRequestInputStream(request); // System.out.println("requestString:" + requestString); HtmlUtil.write(response, "success"); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HtmlUtil.write(response, "this is doGet()"); } }
客户端(请求端)HttpUtil工具类:
import javax.net.ssl.HttpsURLConnection; import java.io.*; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URL; import java.util.Map; public class HttpUtil { /** * 发送http请求并接收返回,字符集默认UTF-8 * * @param url 请求地址 * @param sendData 发送数据 * @param connTimeOut 连接超时 * @param readTimeOut 响应超时 * @return 返回报文 * @throws Exception */ public static String sendAndRcvHttpPost(String url, String sendData, int connTimeOut, int readTimeOut) { return sendAndRcvHttpPost(url, sendData, "UTF-8", connTimeOut, readTimeOut, ""); } /** * 发送http请求并接收返回,(指定字符集) * * @param url 请求地址 * @param sendData 发送数据 * @param charset 字符集 * @param connTimeOut 连接超时 * @param readTimeOut 响应超时 * @param lineSpliter 换行符 * @return 返回报文 * @throws Exception */ public static String sendAndRcvHttpPost(String url, String sendData, String charset, int connTimeOut, int readTimeOut, String lineSpliter) { return sendAndRcvHttpPostBase(url, sendData, charset, connTimeOut, readTimeOut, "application/x-www-form-urlencoded;charset=" + charset, // "text/plain", null, lineSpliter); } public static String sendAndRcvHttpPostBase(String url, String oriData, String charset, int connTimeOut, int readTimeOut, String contentType, Map<String, String> header, String lineSpliter) { Long curTime = System.currentTimeMillis(); String tag = "@" + curTime; String result = ""; BufferedReader in = null; DataOutputStream out = null; int code = 999; HttpsURLConnection httpsConn = null; HttpURLConnection httpConn = null; InputStream httpin = null; try { byte[] sendData = oriData.getBytes(charset); tag = sendData.hashCode() + tag; // Trace.logInfo(Trace.COMPONENT_HTTP, "SimpleHttpConnUtil Prepare:"+tag ); URL myURL = new URL(url); // Trace.logInfo(Trace.COMPONENT_HTTP, "请求地址:"+url); httpConn = (HttpURLConnection) myURL.openConnection(); httpConn.setRequestProperty("Accept-Charset", charset); httpConn.setRequestProperty("user-agent", "Rich Powered/1.0"); if (header != null) { for (String key : header.keySet()) { httpConn.setRequestProperty(key, (String) header.get(key)); } } httpConn.setRequestMethod("POST"); httpConn.setUseCaches(false); httpConn.setRequestProperty("Content-Type", contentType); httpConn.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;"); // httpConn.setRequestProperty("Accept-Encoding","gzip, deflate, sdch"); httpConn.setRequestProperty("Accept-Language", "zh-CN,zh;"); httpConn.setRequestProperty("Cache-Control", "no-cache"); httpConn.setRequestProperty("Pragma", "no-cache"); httpConn.setRequestProperty("Content-Length", sendData.length + ""); httpConn.setConnectTimeout(connTimeOut); httpConn.setReadTimeout(readTimeOut); httpConn.setDoInput(true); httpConn.setInstanceFollowRedirects(true); if (sendData != null) { httpConn.setDoOutput(true); // 获取URLConnection对象对应的输出流 try { httpConn.connect(); } catch (Exception e) { e.printStackTrace(); return null; } out = new DataOutputStream(httpConn.getOutputStream()); // 发送请求参数 out.write(sendData); // flush输出流的缓冲 out.flush(); out.close(); } // 取得该连接的输入流,以读取响应内容 code = httpConn.getResponseCode(); httpin = httpConn.getInputStream(); if (HttpURLConnection.HTTP_OK == code) { String line; in = new BufferedReader(new InputStreamReader(httpin, charset)); while ((line = in.readLine()) != null) { result += line + lineSpliter; } } else { result = null; throw new Exception("支付失败,服务端响应码:" + code); } } catch (SocketTimeoutException e) { // Trace.logError(Trace.COMPONENT_ACTION, "获取返回报文超时!",e); result = "TO"; } catch (Exception e) { // Trace.logError(Trace.COMPONENT_ACTION, "http通讯失败 !",e); result = null; } finally { // Trace.logInfo(Trace.COMPONENT_ACTION,"对方地址:"+url); if (out != null) { try { out.close(); } catch (IOException e) { } } if (httpConn != null) { httpConn.disconnect(); } if (httpsConn != null) { httpsConn.disconnect(); } if (in != null) { try { in.close(); } catch (IOException e) { } } } // Trace.logInfo(Trace.COMPONENT_HTTP, "SimpleHttpConnUtil "+tag+" end for "+(System.currentTimeMillis()-curTime)+"ms"); return result; } }
客户端测试方法:
public class TestMain { public static void main(String[] args) { // String reqData ="p1=a&p2=b&p3=c"; String reqData = "{\"interfaceName\":\"RegisterNotify\",\"merSignMsg\":\"APrWHHydX41atXjfadKBfDPUhKBQbZ6fTKcnHGtVBhe9qpSfArVqRFrlf2wgw9gzmMnGo3x15XKXAZnC51WU60FXNVj2kaxpWYzpuh6rvUDrDVQV6Z7SHEI8GvrMLE8uOG2TPR0Xu6v71o8u8TJsWsiVOP/ncsAHSzSz%%2B2Ch7N3E5ePCQi84To7LvSO5HrtUUmTbc%2BrmG2frJfYJNfvsxuGvt9U2MqmFeWFE98fK5e5SFUSSZLtqj42N18ppSZWSxN3MleGDTsy75zR3JxO6ol99lCPea4zqLmnUoEFlnJ3J6vXXUVXnMuSX5Mw%3D%3D\",\"merchantId\":\"M100002734\",\"tranData\":\"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iR0JLIiA%2FPjxCMkNSZXM%2BPHN0YXR1cz4wPC9zdGF0dXM%2BPGJpbmRTdHM%2BMTwvYmluZFN0cz48Y3VzdG9tZXJJZD5DMTAwMDE0Mjc1PC9jdXN0b21lcklkPjxjb21wYW55PtDs1t3TzrHpzOzPwsLD087Qxc%2Bi18nRr9PQz965q8u%2BPC9jb21wYW55PjxyZWZ1c2VSZWFzb24vPjwvQjJDUmVzPg%3D%3D\",\"version\":\"B2C1.0\"}"; String reqUrl = "http://localhost:8080/test_tomcat_web_war_exploded/form03_6_2_Token_OpenCard_Back"; // String reqUrl="http://localhost:8083/H5api/xx"; String result = HttpUtil.sendAndRcvHttpPost(reqUrl, reqData, 100000, 1000000); System.out.println(result); } }
【测试结论】
Tomcat和jetty对于HttpServletRequest.getParameterMap()的处理不同。jetty可以直接获取到请求参数;而Tomcat获取不到。下面是Tomcat的日志:
六月 11, 2019 10:11:53 上午 org.apache.tomcat.util.http.Parameters processParameters 信息: Character decoding failed. Parameter [{"interfaceName":"RegisterNotify","merSignMsg":"APrWHHydX41atXjfadKBfDPUhKBQbZ6fTKcnHGtVBhe9qpSfArVqRFrlf2wgw9gzmMnGo3x15XKXAZnC51WU60FXNVj2kaxpWYzpuh6rvUDrDVQV6Z7SHEI8GvrMLE8uOG2TPR0Xu6v71o8u8TJsWsiVOP/ncsAHSzSz%%2B2Ch7N3E5ePCQi84To7LvSO5HrtUUmTbc%2BrmG2frJfYJNfvsxuGvt9U2MqmFeWFE98fK5e5SFUSSZLtqj42N18ppSZWSxN3MleGDTsy75zR3JxO6ol99lCPea4zqLmnUoEFlnJ3J6vXXUVXnMuSX5Mw%3D%3D","merchantId":"M100002734","tranData":"PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iR0JLIiA%2FPjxCMkNSZXM%2BPHN0YXR1cz4wPC9zdGF0dXM%2BPGJpbmRTdHM%2BMTwvYmluZFN0cz48Y3VzdG9tZXJJZD5DMTAwMDE0Mjc1PC9jdXN0b21lcklkPjxjb21wYW55PtDs1t3TzrHpzOzPwsLD087Qxc%2Bi18nRr9PQz965q8u%2BPC9jb21wYW55PjxyZWZ1c2VSZWFzb24vPjwvQjJDUmVzPg%3D%3D","version":"B2C1.0"}] with value [] has been ignored. Note that the name and value quoted here may be corrupted due to the failed decoding. Use debug level logging to see the original, non-corrupted values. Note: further occurrences of Parameter errors will be logged at DEBUG level. map count:0
Character decoding failed.Parameter [dd] with value [] has been ignored. 百度翻译为:字符解码失败。已忽略值为[]的参数[dd]。
我的Tomcat版本是7.0.93;jetty版本是6.1.26。通过比较两者的servlet-api.jar,发现Tomcat7的servlet-api的版本是3.0,而jetty的servlet-api的版本是2.5。或许是这种版本的差异导致结果不同。试图跟踪源码来一识庐山真面目,jetty的进去了,但从RequestWapper类里也看不到什么;Tomcat的则进不去。
进一步通过Tomcat测试发现:对于请求参数里的merSignMsg,当我改变其值(去掉开头的一些字符)时,Tomcat就能获取到了。看来还是Tomcat处理字符编码的问题。
【话说回来】
话说回来,上面案例content-type用form实在不合适不地道,因为想获取到请求数据,得取key的值而不是value,这不符合常用的套路啊,由此我的同事那天在接收数据时很折腾了一番。像这种上送json字符串的,改用text/plain更合适,这样的话,接收端通过读取HttpServletRequest的输入流就可以获取到请求数据。
附上读取HttpServletRequest流的代码:
package com; import javax.servlet.http.HttpServletRequest; import java.io.*; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.Map; public class ReadHttpRequest { /** * 从request.ParameterMap获取数据---适用于表单请求 * @param request * @return * @throws UnsupportedEncodingException * @throws Exception */ public static Map getRequestMap(HttpServletRequest request) throws UnsupportedEncodingException,Exception { Map result = new HashMap(); Map<String, String[]> map = request.getParameterMap(); if (0 == map.size()) { System.out.println("未获取到任何请求参数."); // throw new Exception("未获取到任何请求参数."); return result; } for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); ) { Map.Entry element = (Map.Entry) iter.next(); Object strKey = element.getKey(); //key值 String[] value = (String[]) element.getValue(); //value,数组形式 String values = ""; for (int i = 0; i < value.length; i++) { values += "," + value[i]; } result.put(strKey.toString(), values.replaceFirst(",", "")); } return result; } /** * 从输入流获取数据---适用于流请求 * @param request * @return */ public static String getRequestInputStream(HttpServletRequest request) { InputStream in = null; ByteArrayOutputStream out = null; try { in = request.getInputStream(); out = new ByteArrayOutputStream(); in2OutStream(in, out, 1024 * 1024); return out.toString("UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (out != null) out.close(); } catch (IOException e) { e.printStackTrace(); } try { if (in != null) in.close(); } catch (IOException e) { e.printStackTrace(); } } return null; } private static void in2OutStream(InputStream in, OutputStream out, int bufferSize) throws IOException { byte[] buffer = new byte[bufferSize];// 缓冲区 for (int bytesRead = 0; (bytesRead = in.read(buffer)) != -1; ) { out.write(buffer, 0, bytesRead); Arrays.fill(buffer, (byte) 0); } } }
【附】/【BTW】tomcat7控制台日志中文乱码
windows电脑 idea启动Tomcat调试程序时,Tomcat控制台输出里,中文是乱码。
解决办法:
修改Tomcat/bin/catalina.bat文件:
set JAVA_OPTS= 的内容中添加选项-Dfile.encoding=GBK
然后,重启Tomcat试下。如果还不行的话,可尝试改用其他编码格式。
▄︻┻┳═一tomcat与jetty接收请求参数的区别
▄︻┻┳═一比较两种方式的form请求提交
▄︻┻┳═一Post方式的Http流请求调用
当看到一些不好的代码时,会发现我还算优秀;当看到优秀的代码时,也才意识到持续学习的重要!--buguge
本文来自博客园,转载请注明原文链接:https://www.cnblogs.com/buguge/p/11002836.html