属性 值在重新加载网络表单时丢失 (null/empty)
Property value lost (null/empty) on web form reload
使用 spring mvc 3.0.5 我遇到了一个奇怪的(几乎是零星的)数据绑定错误。
在应用程序的一个页面中,使用了一个具有一些原始值(很少的字符串和长整数)和两个列表(我们将它们命名为 A 和 B)的模型 bean。列表显示在两个表中(使用 displaytag)。用户可以编辑一些原始值并提交,显示更新的数据(重新显示同一页面)。列表数据存储在页面的隐藏输入字段中。
问题是有时在 POST/display 循环中会丢失一个隐藏字段。 Java 值变为 null,在 HTML 中变为 value=""。 (它发生在一种 Long 类型和一种 Boolean 类型上)
到目前为止,它只发生在模型属性中的两组特定数据值上。在一种情况下,一个值在第 N 次迭代中消失(N 大约 3 或 4),而在另一种情况下,它发生在第一个 POST 上。
在许多其他数据集中没有观察到问题。好像是未初始化的变量使用,未同步的跨线程数据访问或类似情况。
问题是:
- 这听起来像是某个已知错误吗?
- 如何进行?一步步调试?去哪里看?
更多信息(基本信息,即使使用精确的代码副本也不会重现该问题):
JSP:
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form:form id="form" method="post" modelAttribute="myRequest" action="spremembaUporabnika" enctype="multipart/form-data">
<display:table id="table1" name="myRequest.listA" decorator="si.comtrade.vs.web.decorators.users.UserRightsDecorator">
<display:setProperty name="paging.banner.all_items_found" value=""/>
<display:setProperty name="paging.banner.onepage" value=""/>
<display:column title="Column1">
<c:out value="${myRequest.listA[table1_rowNum-1].orgUnitName}"/>
<form:hidden path="listA[${table1_rowNum-1}].string1" id="string1[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].long1" id="long1[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].long2" id="long2[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].string2" id="string2[${table1_rowNum-1}]"/>
</display:column>
<display:column escapeXml="true" property="prop2" title="Column2" />
...
</display:table>
型号class:
public class MyRequest {
private List<TypeA> listA = new AutoPopulatingList<TypeA>(TypeA.class);
private List<TypeB> listB = new AutoPopulatingList<TypeB>(TypeB.class);
// ... other fields and getters/setters omitted
}
public class TypeA {
Long long1, long2; // <---- one of these loses the value on POST/reload
String string1, string2;
// ... other fields and getters/setters omitted
}
控制器class:
@Controller
@PreAuthorize("hasAnyRole('fooRole')")
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new MyValidator());
binder.registerCustomEditor(Date.class, "listB.date1", new CustomDateEditor(new SimpleDateFormat("dd.MM.yyyy HH:mm"), true));
binder.registerCustomEditor(Date.class, "listB.date2", new CustomDateEditor(new SimpleDateFormat("dd.MM.yyyy HH:mm"), true));
}
@ModelAttribute("myRequest")
public MyRequest getTheRequest() {
MyRequest request = new MyRequest();
List<BarType> barList = getSomeBars(null, null);
request.setBarList(barList);
return request;
}
@RequestMapping(value = "/myPage", method = RequestMethod.GET)
public String myPage(@ModelAttribute("myRequest") MyRequest request,
@RequestParam(value = "userId", required = false) String userId,
HttpSession p_session, Map<String, Object> map) {
... values into myRequest are filled here
return "id_for_tiles";
}
@RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public String doMethod(@ModelAttribute("myRequest") @Valid MyRequest request, BindingResult binder, Map<String, Object> map) {
... do some processing, then redisplay the same page
return "id_for_tiles"; // same as in other method
}
新发现:
- 问题发生在表单被 POST 编辑时:HTML 页面中的值是正确的,但在控制器中它们最终为空
- 在BeanWrapperImpl[=47]中的publicvoid setPropertyValue(PropertyValue pv)方法中发现value为空字符串=]
事实证明,一个重要因素是表单支持文件上传。 (查看答案)
显然这是来自 commons-fileupload-1.2
的 org.apache.commons.fileupload.MultipartStream 中的错误
方法 public int read(byte[] b, int off, int len)(第 878 行)中的代码尝试读取参数值,但是当缓冲区中没有可用数据(由 available() 检查),它调用 makeAvailable() 从 HTTP 请求流中读取更多数据。那可以 return 0、1 或类似的几个字节不足以包含分隔符字符串,所以仍然有 0 个字节 available() 和 read 方法以 -1 退出,而不是从 HTTP 请求流中读取更多数据。
供参考:
/* this is line 877 */
public int read(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new FileItemStream.ItemSkippedException();
}
if (len == 0) {
return 0;
}
int res = available();
if (res == 0) {
res = makeAvailable();
if (res == 0) {
return -1;
}
}
res = Math.min(res, len);
System.arraycopy(buffer, head, b, off, res);
head += res;
total += res;
return res;
}
这是一个已知错误:https://issues.apache.org/jira/browse/FILEUPLOAD-135
已在 1.2.1 版本中修复。
使用 spring mvc 3.0.5 我遇到了一个奇怪的(几乎是零星的)数据绑定错误。 在应用程序的一个页面中,使用了一个具有一些原始值(很少的字符串和长整数)和两个列表(我们将它们命名为 A 和 B)的模型 bean。列表显示在两个表中(使用 displaytag)。用户可以编辑一些原始值并提交,显示更新的数据(重新显示同一页面)。列表数据存储在页面的隐藏输入字段中。
问题是有时在 POST/display 循环中会丢失一个隐藏字段。 Java 值变为 null,在 HTML 中变为 value=""。 (它发生在一种 Long 类型和一种 Boolean 类型上)
到目前为止,它只发生在模型属性中的两组特定数据值上。在一种情况下,一个值在第 N 次迭代中消失(N 大约 3 或 4),而在另一种情况下,它发生在第一个 POST 上。 在许多其他数据集中没有观察到问题。好像是未初始化的变量使用,未同步的跨线程数据访问或类似情况。
问题是:
- 这听起来像是某个已知错误吗?
- 如何进行?一步步调试?去哪里看?
更多信息(基本信息,即使使用精确的代码副本也不会重现该问题):
JSP:
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<form:form id="form" method="post" modelAttribute="myRequest" action="spremembaUporabnika" enctype="multipart/form-data">
<display:table id="table1" name="myRequest.listA" decorator="si.comtrade.vs.web.decorators.users.UserRightsDecorator">
<display:setProperty name="paging.banner.all_items_found" value=""/>
<display:setProperty name="paging.banner.onepage" value=""/>
<display:column title="Column1">
<c:out value="${myRequest.listA[table1_rowNum-1].orgUnitName}"/>
<form:hidden path="listA[${table1_rowNum-1}].string1" id="string1[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].long1" id="long1[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].long2" id="long2[${table1_rowNum-1}]"/>
<form:hidden path="listA[${table1_rowNum-1}].string2" id="string2[${table1_rowNum-1}]"/>
</display:column>
<display:column escapeXml="true" property="prop2" title="Column2" />
...
</display:table>
型号class:
public class MyRequest {
private List<TypeA> listA = new AutoPopulatingList<TypeA>(TypeA.class);
private List<TypeB> listB = new AutoPopulatingList<TypeB>(TypeB.class);
// ... other fields and getters/setters omitted
}
public class TypeA {
Long long1, long2; // <---- one of these loses the value on POST/reload
String string1, string2;
// ... other fields and getters/setters omitted
}
控制器class:
@Controller
@PreAuthorize("hasAnyRole('fooRole')")
public class MyController {
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new MyValidator());
binder.registerCustomEditor(Date.class, "listB.date1", new CustomDateEditor(new SimpleDateFormat("dd.MM.yyyy HH:mm"), true));
binder.registerCustomEditor(Date.class, "listB.date2", new CustomDateEditor(new SimpleDateFormat("dd.MM.yyyy HH:mm"), true));
}
@ModelAttribute("myRequest")
public MyRequest getTheRequest() {
MyRequest request = new MyRequest();
List<BarType> barList = getSomeBars(null, null);
request.setBarList(barList);
return request;
}
@RequestMapping(value = "/myPage", method = RequestMethod.GET)
public String myPage(@ModelAttribute("myRequest") MyRequest request,
@RequestParam(value = "userId", required = false) String userId,
HttpSession p_session, Map<String, Object> map) {
... values into myRequest are filled here
return "id_for_tiles";
}
@RequestMapping(value = "/doSomething", method = RequestMethod.POST)
public String doMethod(@ModelAttribute("myRequest") @Valid MyRequest request, BindingResult binder, Map<String, Object> map) {
... do some processing, then redisplay the same page
return "id_for_tiles"; // same as in other method
}
新发现:
- 问题发生在表单被 POST 编辑时:HTML 页面中的值是正确的,但在控制器中它们最终为空
- 在BeanWrapperImpl[=47]中的publicvoid setPropertyValue(PropertyValue pv)方法中发现value为空字符串=]
事实证明,一个重要因素是表单支持文件上传。 (查看答案)
显然这是来自 commons-fileupload-1.2
的 org.apache.commons.fileupload.MultipartStream 中的错误方法 public int read(byte[] b, int off, int len)(第 878 行)中的代码尝试读取参数值,但是当缓冲区中没有可用数据(由 available() 检查),它调用 makeAvailable() 从 HTTP 请求流中读取更多数据。那可以 return 0、1 或类似的几个字节不足以包含分隔符字符串,所以仍然有 0 个字节 available() 和 read 方法以 -1 退出,而不是从 HTTP 请求流中读取更多数据。
供参考:
/* this is line 877 */
public int read(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new FileItemStream.ItemSkippedException();
}
if (len == 0) {
return 0;
}
int res = available();
if (res == 0) {
res = makeAvailable();
if (res == 0) {
return -1;
}
}
res = Math.min(res, len);
System.arraycopy(buffer, head, b, off, res);
head += res;
total += res;
return res;
}
这是一个已知错误:https://issues.apache.org/jira/browse/FILEUPLOAD-135 已在 1.2.1 版本中修复。