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);
- }
- }
- }
▄︻┻┳═一tomcat与jetty接收请求参数的区别
▄︻┻┳═一比较两种方式的form请求提交
▄︻┻┳═一Post方式的Http流请求调用
tomcat与jetty接收请求参数的区别的更多相关文章
- springMVC中接收请求参数&&数据转发
### 1. 接收请求参数 #### 1.1. [不推荐] 通过HttpServletRequest获取请求参数 假设存在: <form action="handle_login.do ...
- SpringMVC——接收请求参数和页面传参
Spring接收请求参数: 1.使用HttpServletRequest获取 @RequestMapping("/login.do") public String login(Ht ...
- struts2视频学习笔记 11-12(动态方法调用,接收请求参数)
课时11 动态方法调用 如果Action中存在多个方法时,可以使用!+方法名调用指定方法.(不推荐使用) public String execute(){ setMsg("execute&q ...
- Struts系列笔记(6)---action接收请求参数
action接收请求参数 在web开发中,去接收请求参数来获得表单信息非常的常见,自己也总结整理了有关Struts2通过action接收请求参数的几种方法. Struts2 提供三种数据封装的方式: ...
- struts2 action接收请求参数和类型转换
1,action接收请求参数 在struts2中action是什么?(struts2是一个mvc框架) V:jsp M:action C:action ...
- Struts框架(6)---action接收请求参数
action接收请求参数 在web开发中,去接收请求参数来获得表单信息非常的常见,自己也总结整理了有关Struts2通过action接收请求参数的几种方法. Struts2 提供三种数据封装的方式: ...
- SpringMVC之接收请求参数和页面传参
1.Spring接收请求参数 1>.使用HttpServletRequest获取 @RequestMapping("/login.do") public String log ...
- action接收请求参数
一.采用基本类型接收请求参数(get/post)在Action类中定义与请求参数同名的属性,struts2便能接收自动接收请求参数并赋给同名属性. action的代码: public class Pa ...
- SpringMVC接收请求参数和页面传参
接收请求参数: 1,使用HttpServletRequest获取 @RequestMapping("/login.do") public String login(HttpServ ...
随机推荐
- Django 文件下载功能
def file_download(request): con= MySQLdb.connect(host='192.168.xxx.xxx',user='root',passwd='xxxx',db ...
- [深入学习C#]C#实现多线程的方式:使用Parallel类
简介 在C#中实现多线程的另一个方式是使用Parallel类. 在.NET4中 ,另一个新增的抽象线程是Parallel类 .这个类定义了并行的for和foreach的 静态方法.在为 for和 f ...
- javaScript-进阶篇(一)
1.变量 1.必须以字母.下划线或美元符号开头,后面可以跟字母.下划线.美元符号和数字. 2.变量名区分大小写,如:A与a是两个不同变量. 3.不允许使用JavaScript关键字和保留字做变量名. ...
- codeforces 615E Hexagons (二分+找规律)
E. Hexagons time limit per test 1 second memory limit per test 256 megabytes input standard input ou ...
- stl_list.h
stl_list.h // Filename: stl_list.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com // Blog: http://b ...
- 微信非全屏播放设置(仅Iphone)
由于微信X5内核强制视频全屏,用X5自带内核播放,一般内嵌视频打开播放就会被全屏. ihpone里面可以通过设置 x-webkit-airplay="true" webkit-pl ...
- codevs 3372 选学霸
3372 选学霸 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 老师想从N名学生中选M人当学霸,但有K对人实力相当,如果 ...
- UILabel的富文本显示选项
UILabel的富文本格式设置 1.实例化方法和使用方法 实例化方法: 使用字符串初始化 - (id)initWithString:(NSString *)str; 例: NSMutableAttri ...
- vc++ 访问php webService
之前做了一个VC++访问c#制作的WebService,没有问题,接着我又做了一个VC++访问php制作的WebService ,结果老是出现Client错误.这个php WebService是用Ze ...
- bzoj 1070 修车 —— 费用流
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1070 需要考虑前面修的车对后面等待的车造成的时间增加: 其实可以从每个人修车的顺序考虑,如果 ...