打印

CM2001: 不能保证带有 name 属性的 BUTTON / INPUT [ type = submit / button / image ] 元素的 value 属性值在所有浏览器中都可以被提交到服务端

作者:陆远 孙东国

标准参考

表单元素是 HTML 文档中最常用的元素,根据 W3C HTML4.01 规范中的描述,表单(FORM)元素是文档(Document)的一部分,它可以包含常规文本内容、其他 HTML 标记、控件及 LABEL 元素。用户通常在对表单内的空间进行填写或修改之后提交给某一个代理(agent,如 Web 服务器、Mail 服务器)进行处理。

表单的提交过程主要分为以下四步:

  1. 确定可以被提交的 successful controls
  2. 建立表单数据集(form data set),表单数据集是一组根据 successful controls 建立的键值对(控件名“key”/控件当前值“value”)序列;
  3. 对 form data set 进行编码,编码依据为 FORM 元素的 enctype 属性中所指定的 content type
  4. 提交编码后的 form data set,最终编码后的数据根据表单元素的 method 属性指定的协议发送到 action 属性所指的目标处理代理(processing agent)。

successful controls 是指可以有效的提交的控件。每一个 successful controls 均具备由其 name 属性(control name)及当前 value 属性(current value)所组成的“name/value”键值对,这些 successful controls 的键值对组成了 form data set。而 W3C 规定,定义在 FORM 元素内的 successful controls 必须拥有 name 属性。

规范还做出了如下规定:

  • 被设置为失效(disabled 属性)的控件不是 successful controls
  • 如果一个表单内包含多个提交按钮(submit button),则只有当前被激活的提交按钮 successful controls
  • 所有被选中(on)的复选框(checkbox successful controls
  • 对于共享同一个 name 属性的单选框(radio),只有被选中(on)的 successful controls
  • 对于下拉列表菜单(SELECT 元素),在其有 OPTION 被选中时 successful controls,否则不是 successful controls
  • 文件选择(file select)组件的当前 value 属性是一个或多个文件名组成的列表,当表单提交时,文件内容与其他表单数据一同被提交,文件内容的编码方式由 FORM 元素的 content type 决定。

若一个控件在表单提交时没有当前 value 属性,不要求用户端一定将其视为 successful controls

同时规范中特别提到重置按钮(reset button不应该被认作 successful controls。而隐藏域(hidden)及被隐藏的符合 successful controls 条件的控件虽然不会被渲染,但仍然应被认为是 successful controls

关于 表单 的详细信息,请参考 HTML4.01 规范 17 Forms 中的内容。

问题描述

IE Firefox Opera 在自定义提交方式下不会提交普通按钮及提交按钮的键值对信息;而 Chrome Safari 会将这些信息也提交给服务器端。

造成的影响

通常情况下,不同浏览器在表单提交时 form data set 中键值对的差异不会造成太大问题,但若服务器端程序对浏览器端提交来的 form data set 进行较严格的判断时,可能会造成在某些浏览器中表单提交异常。如:

一般在登录表单中,我们都会使用自定义 onsubmit 事件的方式,此方式通过 JavaScript 为用户输入的表单信息作初步的验证,且提交方式多数为 POST 方式,当提交按钮有 name 属性时,IE、Firefox、Opera 均不会将提交按钮自身的“name/value”添加到 form data set 中,Chrome 和 Safari 则会将提交按钮自身的信息加入 form data set。这时候服务器端收到的 request header 就会不同,若目的地址的服务端程序对提交来的 form data set 进行了严格的判断,这时 Chrome 和 Safari 提交的信息中比其他浏览器多出的提交按钮的键值对就有可能造成登录失败。

受影响的浏览器

Chrome Safari  

问题分析

在本地创建一个 Web 服务器,编写四个静态页面:

<form action="submit.php" method="METHOD"  onsubmit="ONSUBMIT" target="iframe1" enctype="multipart/form-data">
    <iframe name="iframe1"></iframe>
    <input type="submit" name="I0" value="提交" />
    <input type="text" name="I1" value="input_text" />
    <input type="password" name="I2" value="input_password" />
    <input type="checkbox" name="I3" checked />
    <input type="radio" name="I4" checked />
    <input type="hidden" name="I5" value="input_hidden" />
    <input type="image" name="I6" src="google.gif" value="input_image" />
    <input type="button" name="I7" value="input_button" />
    <input type="file" name="I8" value="input_file" />
    <input type="reset" name="I9" value="input_reset" />
    <input type="text" name="I10" />
    <input type="file" name="I11" />
    <input type="radio" name="I12" />
    <input type="checkbox" name="I13" />
    <select name="S1"><option>select</option></select>
    <select name="S2"></select>
    <button name="B1" type="button">button</button>
    <textarea name="T1">textarea</textarea>
</form>

静态页面实际为四组 FORM 元素,区别为 method 属性及 onsubmit 事件:

  onsubmit="" onsubmit="this.submit(); return false;"
method="get" 第一组“form1.html” 第二组“form2.html”
method="post" 第三组“form3.html” 第四组“form4.html”

各组中均是由一个 FORM 元素,将其内部一系列表单控件元素的 form data set 提交到“submit.php”,并在 IFRAME 元素中显示提交后的结果页面。

服务器端动态页面“submit.php”代码如下:

<?php
  $gp = $_REQUEST;
  foreach ($gp as $k=>$v) {
    echo $k . "/" . $v . "<br />";
  }
  $fp = $_FILES;
  foreach ($fp as $kf=>$kv) {
    echo $kf . "/" . $kv["name"] . "<br />";
  }
?>

“submit.php”的作用是将客户端表单提交来的 form data set 遍历打印出来。

这段代码在不同的浏览器环境中的表现如下(绿色背景文字代表服务器端返回的有效数据,红色背景文字代表服务器端未返回的数据):

第一组、第三组
点击“I0”提交
IE6/7, IE8(Q) IE8(S), Firefox, Opera Chrome, Safari
提交按钮 提交 提交 提交
带内容的文本框 提交 提交 提交
带内容的密码框 提交 提交 提交
选中的复选框 提交 提交 提交
选中的单选框 提交 提交 提交
带内容的隐藏域 提交 提交 提交
提交图片 不提交 不提交 不提交
INPUT普通按钮 不提交 不提交 不提交
已选文件的文件组件 提交 提交 提交
重置按钮 不提交 不提交 不提交
空的文本框 提交 提交 提交
未选文件的文件组件 提交 提交 提交
未选中的复选框 不提交 不提交 不提交
未选中的单选框 不提交 不提交 不提交
有选中的下拉框 提交 提交 提交
未选中的下拉框 不提交 不提交 不提交
BUTTON普通按钮 提交1 不提交 不提交
多行文本框 提交 提交 提交

第一组、第三组
点击“I6”提交
IE6/7, IE8(Q) IE8(S), Opera Firefox, Chrome, Safari
提交按钮 不提交 不提交 不提交
带内容的文本框 提交 提交 提交
带内容的密码框 提交 提交 提交
选中的复选框 提交 提交 提交
选中的单选框 提交 提交 提交
带内容的隐藏域 提交 提交 提交
提交图片 提交2 提交2 提交
INPUT普通按钮 不提交 不提交 不提交
已选文件的文件组件 提交 提交 提交
重置按钮 不提交 不提交 不提交
空的文本框 提交 提交 提交
未选文件的文件组件 提交 提交 提交
未选中的复选框 不提交 不提交 不提交
未选中的单选框 不提交 不提交 不提交
有选中的下拉框 提交 提交 提交
未选中的下拉框 不提交 不提交 不提交
BUTTON普通按钮 提交 不提交 不提交
多行文本框 提交 提交 提交

第二组、第四组
点击“I0”提交
IE6/7, IE8(Q) IE8(S), Firefox, Opera Chrome, Safari
提交按钮3 不提交 不提交 提交
带内容的文本框 提交 提交 提交
带内容的密码框 提交 提交 提交
选中的复选框 提交 提交 提交
选中的单选框 提交 提交 提交
带内容的隐藏域 提交 提交 提交
提交图片 不提交 不提交 不提交
INPUT普通按钮 不提交 不提交 不提交
已选文件的文件组件 提交 提交 提交
重置按钮 不提交 不提交 不提交
空的文本框 提交 提交 提交
未选文件的文件组件 提交 提交 提交
未选中的复选框 不提交 不提交 不提交
未选中的单选框 不提交 不提交 不提交
有选中的下拉框 提交 提交 提交
未选中的下拉框 不提交 不提交 不提交
BUTTON普通按钮 提交 不提交 不提交
多行文本框 提交 提交 提交

第二组、第四组
点击“I6”提交
IE6/7, IE8(Q) IE8(S), Firefox, Opera Chrome, Safari
提交按钮 不提交 不提交 不提交
带内容的文本框 提交 提交 提交
带内容的密码框 提交 提交 提交
选中的复选框 提交 提交 提交
选中的单选框 提交 提交 提交
带内容的隐藏域 提交 提交 提交
提交图片3 不提交 不提交 提交
INPUT普通按钮 不提交 不提交 不提交
已选文件的文件组件 提交 提交 提交
重置按钮 不提交 不提交 不提交
空的文本框 提交 提交 提交
未选文件的文件组件 提交 提交 提交
未选中的复选框 不提交 不提交 不提交
未选中的单选框 不提交 不提交 不提交
有选中的下拉框 提交 提交 提交
未选中的下拉框 不提交 不提交 不提交
BUTTON普通按钮 提交 不提交 不提交
多行文本框 提交 提交 提交

【注】
1. 在 IE6、IE7、IE8(Q) 下,始终认为 BUTTON 元素的普通按钮 successful control其他浏览器始终认为 BUTTON 元素的普通按钮不是 successful control;。
2. 在 IE、Opera 下,始终不提交 INPUT 图片的 value 属性;
3. 在 IE、Firefox、Opera 下,使用“form.submit()”方法自定义提交时(第二组、第四组),浏览器认为提交按钮或图片不是 successful controlChrome、Safari 下,始终认为提交按钮或图片 successful control,且会同时将其 value 属性的值也放入 form data set。

解决方案

通常情况下,服务器端不需要按钮的 key/value 信息,建议删除按钮的 name 属性,不使其成为 successful control

参见

知识库

相关问题

测试环境

操作系统版本: Windows 7 Ultimate build 7600
浏览器版本: IE6
IE7
IE8
Firefox 3.6
Chrome 5.0.342.2 dev
Safari 4.0.4
Opera 10.50
测试页面: form1.html
form2.html
form3.html
form4.html
本文更新时间: 2010-08-02

关键字

form submit 表单 key value 键值 提交 按钮 input name