← HTML 表单:从控件到提交与校验 / 提交算法:entry list 与三种编码 待审核 6 / 24
entry list · enctype

提交算法:entry list 与三种编码

点提交那一刻,浏览器并不是把整张表单原样打包,而是先跑一遍规范的提交算法:按 tree order 遍历表单里的 submittable elements,把每个成功的控件(successful control)变成一条 name=value 的 entry,凑成 entry list;再由 method 决定它去 URL 的 query 还是请求体,由 enctype 决定请求体怎么编码。很多「为什么这个字段没发出去」「为什么 file 上传必须改 enctype」的问题,根都在这一段算法里。本页用一个 live form,把 entry list 的构造、跳过规则,以及三种 enctype 的真实报文当场拼给你看。承接 <form> 元素的 form ownership 与 按钮类型与隐式提交的 submitter。

1 · 构造 entry list:哪些控件「成功」,哪些被跳过

下面是一张 live form。改任意字段,右侧实时显示 new FormData(form) 取到的真实 entry list——它就是浏览器提交时会发出去的那一份。表里故意混进了会被跳过的控件:没写 name 的、disabled 的、没勾的 checkbox。它们在 form.elements 里,却不进 entry list。勾上那个没写 value 的 checkbox,注意它发的是 name=on<select multiple> 选中几个就发几条。

哪些是 submittable element。只有 button · input · select · textarea 以及 form-associated custom element 是 submittable——算法只遍历这些。<output> · <fieldset> · <object> 虽在 form.elements 里(它们是 listed element),却不是 submittable,提交时一律跳过。

2 · 被跳过的情形

遍历到一个 submittable element,规范会逐条检查它是否「成功」。下面五种任意命中就整个跳过,不产生 entry。上面 live form 里能直接看到前三种:

跳过的情形 原因
没有 name / name 为空 entry 的 key 就是 name,没有 name 无从成 entry。脚本仍可经 form.elements 访问它,但不提交。
disabled 被禁用的控件(含位于 disabled <fieldset> 内的)一律不收集。
未选中的 checkbox / radio 只有 checked 的复选 / 单选才成功;一组 radio 里没选中的、以及没勾的 checkbox 都不发。
不是本次 submitter 的按钮 表单里多个 submit 按钮,只有真正触发提交的那一个发自己的 name=value,其余按钮不发。
<datalist> 内的 option <datalist> 只是输入建议来源,其中的 <option> 不是控件,从不进 entry list。

几个值的细节。勾选但没写 value 属性的 checkbox 提交 name=on(规范默认值);<input type="file"> 的 entry 是文件名 + 文件内容,只能用 multipart 编码运送;submit 按钮发自己的 name=value<input type="image"> 提交的不是 value,而是点击位置的像素坐标 name.x / name.y 两条 entry。

3 · method 决定去向,enctype 决定编码

entry list 凑好后,分两条路。method="get":把 entry list 序列化成 application/x-www-form-urlencoded,作为 query string 接到 action URL 后面——会覆盖 action 里原有的 query。method="post":entry list 进请求体,具体编码由 enctype 决定。下面切换 method 与 enctype,右侧实时按选择渲染:get 时拼出完整 URL,post 时渲染请求头 + body 报文。改上面 live form 的任意字段,这里同步重算。

4 · 三种 enctype 的报文对照

enctype 报文形态 适用
application/x-www-form-urlencoded 默认。q=hello&category=docs 一串;空格编码成 +,非 ASCII / 保留字符走 percent-encode。 绝大多数纯文本表单;get 提交也用这套序列化。
multipart/form-data boundary 分隔多段,每段一个 Content-Disposition: form-data; name="…";file 段额外带 filenameContent-Type 文件上传必须用它;字段含大量二进制 / 大文本时也更合适。
text/plain 每行一条 name=value(CRLF 分隔),完全不转义 仅供调试 / 生成邮件正文;不能用于真实 HTTP 提交。

file 上传只能 multipart。<input type="file"> 的内容是字节流,urlencoded 与 text/plain 都无法承载文件体(urlencoded 只会发出文件名当字符串,内容丢失)。规范要求:表单含 file 控件且有文件被选中时,enctype 必须设为 multipart/form-data,否则文件根本传不到服务端。

text/plain 不转义,不要用于真实提交。它把 name=value 原样逐行拼接,值里若含 = / 换行 / boundary 样式的文本,服务端无法可靠地切分,且没有任何转义保护。规范保留它主要是历史与 mailto: action 生成邮件正文的场景,任何走 HTTP 的真实接口都应使用 urlencoded 或 multipart。

dirname 把文本方向一并告诉服务端。<input name="comment" dirname="comment.dir"> 在提交时,除了正常的 comment=comment=\dots,还会额外发一条 comment.dir=ltrcomment.dir=rtl,取自该控件渲染时的实际方向。服务端据此知道这段用户输入该按从左到右还是从右到左展示——这对会同时接收阿拉伯语 / 希伯来语等 RTL 文本的输入框尤其有用。上面 live form 的 comment 字段填了阿拉伯语,留意 entry list 里那条 comment.dir