属性 值在重新加载网络表单时丢失 (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
  }

新发现:

事实证明,一个重要因素是表单支持文件上传。 (查看答案)

显然这是来自 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 版本中修复。