web基础---->request的请求参数分析
当contentType为application/json的时候,在servlet中通过request.getParameter得到的数据为空。今天我们就java的请求,分析一下request得到参数的过程。如果你还不知道自己喜欢什么,你就真的迷失了。
java的请求参数
首先我把环境代码贴出来,如下前端jsp的代码:$.ajax方法里面默认的contenttype为:application/x-www-form-urlencoded
<script type="text/javascript" src="js/jquery-3.1.0.js"></script>
<script type="text/javascript">
function testJson() {
$.ajax({
url: "ParameterServlet",
type: "post",
data: {
"username": "huhx",
"love": "javascript"
}
// contentType: "application/json"
});
}
</script>
</head>
<body>
hello world.
<input type="text" name="username"><br>
<button onclick="testJson()">love you, huhx</button>
</body>
</html>
后端java的代码:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println(request.getContentType());
String username = request.getParameter("username");
System.out.println(username);
}
一、request.getParameter("username")执行的实际代码其实是Request类的getParameter方法:
public String getParameter(String name) {
if (!parametersParsed) {
parseParameters();
}
return coyoteRequest.getParameters().getParameter(name);
}
parametersParsed是一个为false的全局变量,由于是线程的问题。parseParameters()针对这个请求应该只会执行一次。这里关键代码有两个,其一如下:
String contentType = getContentType();
if (contentType == null) {
contentType = "";
}
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
} else {
contentType = contentType.trim();
} if ("multipart/form-data".equals(contentType)) {
parseParts();
success = true;
return;
} if (!("application/x-www-form-urlencoded".equals(contentType))) {
success = true;
return;
}
这段代码不难理解,首先会得到请求类型。从上述代码可以得出两点:
- 当contenttype有多种时,只会取第一种。比如contenttype为application/json;application/x-www-form-urlencoded,最终是application/json
- 当contenttype不为application/x-www-form-urlencoded,直接return掉了。也就是说关键代码二不执行了。
- 当contenttype为multipart/form-data时,parseParts()方法里使用的解析文件的框架是apache自带的fileupload。
好了,以下贴出关键二的代码:
formData = readChunkedPostBody();
parameters.processParameters(formData, 0, len)
- readChunkedPostBody方法,其实就是request.getInputStream().read()方法,将请求的数据通过流的方式读取出来。为了方便,下面贴出代码:
protected byte[] readChunkedPostBody() throws IOException {
ByteChunk body = new ByteChunk();
byte[] buffer = new byte[CACHED_POST_LEN];
int len = 0;
while (len > -1) {
len = getStream().read(buffer, 0, CACHED_POST_LEN); // getStream()返回的是CoyoteInputStream,一种带有缓存的流。
if (connector.getMaxPostSize() >= 0 &&
(body.getLength() + len) > connector.getMaxPostSize()) {
// Too much data
checkSwallowInput();
throw new IllegalStateException(
sm.getString("coyoteRequest.chunkedPostTooLarge"));
}
if (len > 0) {
body.append(buffer, 0, len);
}
}
if (body.getLength() == 0) {
return null;
}
if (body.getLength() < body.getBuffer().length) {
int length = body.getLength();
byte[] result = new byte[length];
System.arraycopy(body.getBuffer(), 0, result, 0, length);
return result;
}
return body.getBuffer();
}
- processParameters()是在Parameters类里面的方法,做的工作就是对请求的数据,做key与value的拆分,然后存放进一个名叫paramHashValues的Map中。后续的request.getParameter取的就是paramHashValues里面的数据
ArrayList<String> values = paramHashValues.get(key);
if (values == null) {
values = new ArrayList<String>(1);
paramHashValues.put(key, values);
}
values.add(value);
解析过程我就不分析了,感兴趣的可以自己查看源码参见tomcat的源码:Parameters类processParameters方法。代码如下:
private void processParameters(byte bytes[], int start, int len, Charset charset) {
if(log.isDebugEnabled()) {
log.debug(sm.getString("parameters.bytes",
new String(bytes, start, len, DEFAULT_CHARSET)));
}
int decodeFailCount = 0;
int pos = start;
int end = start + len;
while(pos < end) {
int nameStart = pos;
int nameEnd = -1;
int valueStart = -1;
int valueEnd = -1;
boolean parsingName = true;
boolean decodeName = false;
boolean decodeValue = false;
boolean parameterComplete = false;
do {
switch(bytes[pos]) {
case '=':
if (parsingName) {
// Name finished. Value starts from next character
nameEnd = pos;
parsingName = false;
valueStart = ++pos;
} else {
// Equals character in value
pos++;
}
break;
case '&':
if (parsingName) {
// Name finished. No value.
nameEnd = pos;
} else {
// Value finished
valueEnd = pos;
}
parameterComplete = true;
pos++;
break;
case '%':
case '+':
// Decoding required
if (parsingName) {
decodeName = true;
} else {
decodeValue = true;
}
pos ++;
break;
default:
pos ++;
break;
}
} while (!parameterComplete && pos < end);
if (pos == end) {
if (nameEnd == -1) {
nameEnd = pos;
} else if (valueStart > -1 && valueEnd == -1){
valueEnd = pos;
}
}
if (log.isDebugEnabled() && valueStart == -1) {
log.debug(sm.getString("parameters.noequal",
Integer.valueOf(nameStart), Integer.valueOf(nameEnd),
new String(bytes, nameStart, nameEnd-nameStart,
DEFAULT_CHARSET)));
}
if (nameEnd <= nameStart ) {
if (valueStart == -1) {
// &&
if (log.isDebugEnabled()) {
log.debug(sm.getString("parameters.emptyChunk"));
}
// Do not flag as error
continue;
}
// &=foo&
UserDataHelper.Mode logMode = userDataLog.getNextMode();
if (logMode != null) {
String extract;
if (valueEnd > nameStart) {
extract = new String(bytes, nameStart, valueEnd
- nameStart, DEFAULT_CHARSET);
} else {
extract = "";
}
String message = sm.getString("parameters.invalidChunk",
Integer.valueOf(nameStart),
Integer.valueOf(valueEnd), extract);
switch (logMode) {
case INFO_THEN_DEBUG:
message += sm.getString("parameters.fallToDebug");
//$FALL-THROUGH$
case INFO:
log.info(message);
break;
case DEBUG:
log.debug(message);
}
}
setParseFailedReason(FailReason.NO_NAME);
continue;
// invalid chunk - it's better to ignore
}
tmpName.setBytes(bytes, nameStart, nameEnd - nameStart);
if (valueStart >= 0) {
tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart);
} else {
tmpValue.setBytes(bytes, 0, 0);
}
// Take copies as if anything goes wrong originals will be
// corrupted. This means original values can be logged.
// For performance - only done for debug
if (log.isDebugEnabled()) {
try {
origName.append(bytes, nameStart, nameEnd - nameStart);
if (valueStart >= 0) {
origValue.append(bytes, valueStart, valueEnd - valueStart);
} else {
origValue.append(bytes, 0, 0);
}
} catch (IOException ioe) {
// Should never happen...
log.error(sm.getString("parameters.copyFail"), ioe);
}
}
try {
String name;
String value;
if (decodeName) {
urlDecode(tmpName);
}
tmpName.setCharset(charset);
name = tmpName.toString();
if (valueStart >= 0) {
if (decodeValue) {
urlDecode(tmpValue);
}
tmpValue.setCharset(charset);
value = tmpValue.toString();
} else {
value = "";
}
try {
addParameter(name, value);
} catch (IllegalStateException ise) {
// Hitting limit stops processing further params but does
// not cause request to fail.
UserDataHelper.Mode logMode = maxParamCountLog.getNextMode();
if (logMode != null) {
String message = ise.getMessage();
switch (logMode) {
case INFO_THEN_DEBUG:
message += sm.getString(
"parameters.maxCountFail.fallToDebug");
//$FALL-THROUGH$
case INFO:
log.info(message);
break;
case DEBUG:
log.debug(message);
}
}
break;
}
} catch (IOException e) {
setParseFailedReason(FailReason.URL_DECODING);
decodeFailCount++;
if (decodeFailCount == 1 || log.isDebugEnabled()) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("parameters.decodeFail.debug",
origName.toString(), origValue.toString()), e);
} else if (log.isInfoEnabled()) {
UserDataHelper.Mode logMode = userDataLog.getNextMode();
if (logMode != null) {
String message = sm.getString(
"parameters.decodeFail.info",
tmpName.toString(), tmpValue.toString());
switch (logMode) {
case INFO_THEN_DEBUG:
message += sm.getString("parameters.fallToDebug");
//$FALL-THROUGH$
case INFO:
log.info(message);
break;
case DEBUG:
log.debug(message);
}
}
}
}
}
tmpName.recycle();
tmpValue.recycle();
// Only recycle copies if we used them
if (log.isDebugEnabled()) {
origName.recycle();
origValue.recycle();
}
}
if (decodeFailCount > 1 && !log.isDebugEnabled()) {
UserDataHelper.Mode logMode = userDataLog.getNextMode();
if (logMode != null) {
String message = sm.getString(
"parameters.multipleDecodingFail",
Integer.valueOf(decodeFailCount));
switch (logMode) {
case INFO_THEN_DEBUG:
message += sm.getString("parameters.fallToDebug");
//$FALL-THROUGH$
case INFO:
log.info(message);
break;
case DEBUG:
log.debug(message);
}
}
}
}
- 由于上述分析的contenttype不为form-data的和x-www-form-urlencoded的不会执行关键二的代码,所以对于请求类型为application/json通过request.getParameter得到的数据为空。
tomcat对请求数据是通过一个个字符判断的,PE的处理方式是通过正则表达式的。关于正则表达式可以参见博客:平安夜快乐
二、request.getAttribute()其实就是RequestFacade类的getAttribute
首先看一下request.setAttribute(name, value)的方法:
public void setAttribute(String name, Object value) {
// Name cannot be null
if (name == null) {
throw new IllegalArgumentException
(sm.getString("coyoteRequest.setAttribute.namenull"));
}
// Null value is the same as removeAttribute()
if (value == null) {
removeAttribute(name);
return;
}
// Special attributes,它的名字一般是tomcat内部的属性,都是心org.apache.catalina开头的。
SpecialAttributeAdapter adapter = specialAttributes.get(name);
if (adapter != null) {
adapter.set(this, name, value);
return;
}
// 安全的检查
...........
Object oldValue = attributes.put(name, value);
// Pass special attributes to the native layer
if (name.startsWith("org.apache.tomcat.")) {
coyoteRequest.setAttribute(name, value);
}
// Notify interested application event listeners
notifyAttributeAssigned(name, value, oldValue);
}
这里的attributes实质上是一个Map,一个多线程安全的Map。至于getAttribute其实就是从Map中得到数据。
private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
友情链接
web基础---->request的请求参数分析的更多相关文章
- tomcat源码---->request的请求参数分析
当contentType为application/json的时候,在servlet中通过request.getParameter得到的数据为空.今天我们就java的请求,分析一下request得到参数 ...
- SpringBoot系列教程web篇之Post请求参数解析姿势汇总
作为一个常年提供各种Http接口的后端而言,如何获取请求参数可以说是一项基本技能了,本篇为<190824-SpringBoot系列教程web篇之Get请求参数解析姿势汇总>之后的第二篇,对 ...
- SpringBoot系列教程web篇之Get请求参数解析姿势汇总
一般在开发web应用的时候,如果提供http接口,最常见的http请求方式为GET/POST,我们知道这两种请求方式的一个显著区别是GET请求的参数在url中,而post请求可以不在url中:那么一个 ...
- 012-Spring Boot web【一】web项目搭建、请求参数、RestController、使用jsp、freemarker,web容器tomcat和jetty
一.项目搭建 同:http://www.cnblogs.com/bjlhx/p/8324971.html 1)新建maven项目→使用默认配置即可 定义好项目名称等 2)修改jdk版本 <pro ...
- request 获取请求参数
/** * 根据request获取请求的用户参数 * @return * @return */ protected <T> T getParamConvertEntity(Class cl ...
- java web项目get,post请求参数中文乱码解决
[转载]原文地址:https://www.cnblogs.com/tom-plus/p/6392279.html 在开发过程中,有时候会碰到get,post请求参数中文乱码. 原因: Http请求传输 ...
- 入门servlet:request获取请求参数通用方式
一.获取请求参数通用方式 1. String getParameter(String name):根据参数名称获取参数值 例:username=flypig&password=2343 2. ...
- 转发:tomcat的acess_log打印post请求参数,分析日志
转载自:https://blog.csdn.net/qq_30121245/article/details/52861935 1) 在项目中加入相应的包和类,加载那里无所谓,只要web.xml配置正确 ...
- request获取请求参数通用方式
package com.hopetesting.web.request;import javax.servlet.ServletException;import javax.servlet.annot ...
随机推荐
- sshd 错误
140669086946976:error:0E079065:configuration file routines:DEF_LOAD_BIO:missing equal sign:conf_def. ...
- 如何安装Docker UCP
parallels@ubuntu:~/Downloads/Docker$ ls DTR UCP parallels@ubuntu:~/Downloads/Docker$ sudo openssl re ...
- jdbc autoReconnect=true 参数设置导致 slow log 爆表。
1.过程 同事按照文档上配置了下面的jdbc url: jdbc:mysql://ip:port/db?autoReconnect=true&useUnicode=true&chara ...
- unity如何显示血条(不使用NGUI)
用unity本身自带的功能,如何显示血条? 显示血条,从资源最小化的角度,只要把一个像素的色点放大成一个矩形就足够,三个不同颜色的矩形,分别显示前景色,背景色,填充色,这样会消耗最少的显存资源. un ...
- 解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)
解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几 ...
- Visual Studio Xamarin编译Android项目出错的解决办法
安装完Xamarin后,编译Android项目时,你会发现好长时间进度都不动,当你取消编译后,会发现其实是出错了,就是因在Android项目在第一次编译时要去google网站上下一个andorid s ...
- sql server 写性能优化
潜在的方案是: (1)2014版本的内存表 (2)表分区,将表数据存放在多个硬盘上. (3)采用SSD磁盘阵列. (4)不重要的表关闭事务日志.??
- Win10 UWP开发中的重复性静态UI绘制小技巧 2
小技巧1 地址:http://www.cnblogs.com/ms-uap/p/4641419.html 介绍 我们在上一篇博文中展示了通过Shape.Stroke族属性实现静态重复性UI绘制,使得U ...
- 换个角度理解云计算之MapReduce(二)
接上篇 3.Combiner操作 前面讲完Map操作,总结一下就是:一个大文件,分成split1~5,对应于Map1~5,每一个Map处理一个split,每一个split的每一行,会用每一个Map的m ...
- js 函数覆盖的问题
今天遇到奇怪问题是,一个html里面引入一个新的js进来后,原来的按钮点击后(假设触发onclick函数)在某个地方卡住了,不往下执行了.—— 之前都是好好的. 调试发现,在onclick中间某一处调 ...