HTML – Native Form 原生表单功能集
前言
以前写过 , 但很不齐全, 这篇想做一个大整理. 主要讲讲在网站中使用原生 Form 的功能, 不足和扩展.
前端是原生的 HTML/JS, 后端是 ASP.NET Core Razor Pages.
Simplest Form Overview
form 的职责是让 user 可以把信息传递到服务端. 常见的使用场景是 contact / enquiry form.
结构大概长这样
<form method="post"> <input type="text" name="username"> <button type="submit">Submitbutton> form>
一个 form tag 把所有信息包裹在里面
input text 作为 accessor 信息读写器. 还有很多种 accessor 用来输出输入不同种类的信息. 后面会详细讲到.
一个 submit button.
ASP.NET Core
public class FormData { public string Username { get; set; } = ""; } public class IndexModel : PageModel { public void OnGet() { } public void OnPost([FromForm] FormData formData) { var value = formData.Username; } }
一个对象从 form 获取信息, 然后就可以做各做操作了, 比如存入数据库, 发电邮等等.
Form Attribute
form 有 3 个常用的 attribute
method
有 3 种 get, post, dialog
get 我没有用过, 也不知道什么时候会用到.
post 是每次用的
dialog 很新, Safari 15.4 (14-03-2022) 才支持, 我没有用过这篇就不介绍了.
action
action 用来声明 post 去服务端的地址. 没有填写的话就是和当前页面相同地址.
比如 current url : /contact, 那么就是 post to /contact
注: 在 Razor Pages, 如果 post to other page 需要加多一个 attribute asp-antiforgery, 不然会报错 400 error 哦, 详情可以看这篇
<form method="post" action="/other-page" asp-antiforgery="true">
enctype
声明 form 的格式, 类似 Content-Type
application/x-www-form-urlencoded (默认): 信息会以 key-value encodeURIComponent 方式发送出去, 这个格式不支持文件上传哦.
multipart/form-data: 想上传文件就要用这个 (即使没有文件也是可以用的, 只是会有一些多余的信息, 比如分隔符号, 那个是为了 upload file 才需要的)
例子
<form action="/" method="post" enctype="multipart/form-data" asp-antiforgery="true"> <input type="text" name="username"> <input type="file" name="attachment"> <input type="submit" value="Submit"> form>
ASP.Net Core
public class FormData { public string Username { get; set; } = ""; public IFormFile Attachment { get; set; } = null!; } public class IndexModel : PageModel { public void OnGet() { } public void OnPost([FromForm] FormData formData) { var value = formData.Username; var fileSize = formData.Attachment.Length; } }
Form Submission
Submit Button
<button>Submitbutton> <button type="submit">Submitbutton> <input type="submit" value="Submit">
这 3 个效果是一样的.
button 默认的 type 就是 submit, 所以 1 和 2 是一样的.
input:submit 和 button 也是一样的, style 都一样.
通常我会用第 2 个. 比较明确.
multiple button in form
复杂的 form 里面会有多个 button 出现. 但通常只会有一个用来 submit.
所以其余的记得要写上 type="button".
input enter trigger submit button click
在任何一个 input (accessor) 按 enter 键, 游览器会找到 form 里面第一个 submit button (type=submit / image / no defined) 点击.
multiple submit button
这个不顺风随, 比较好的做法是用 select 让用户选择提交类型, 而不是使用 button 作为类型. 但我还是给个例子
<form method="post"> <input type="text" name="dataName"> <button type="submit" name="deleteType" value="SoftDelete">Soft Delete Databutton> <button type="submit" name="deleteType" value="HardDelete">Hard Delete Databutton> form>
在 button 声明 name 和 value, 用户点击后, 被点击的那一个 submit value 会被放入信息中, 一起发送出去
ASP.NET Core
public enum DeleteType { SoftDelete, HardDelete } public class FormData { public string? DataName { get; set; } public DeleteType? DeleteType { get; set; } } public class IndexModel : PageModel { public void OnGet() { } public void OnPost([FromForm] FormData formData) { } }
listen to submission event
通过 JS 可以拦截 submit event
document.querySelector("form").addEventListener("submit", (e) => { e.preventDefault(); alert("submit"); });
通过 preventDefault 可以阻止游览器发送到服务端. 通常目的是想改成使用 Ajax 发送, 下面会说到细节.
JS trigger submission
document.querySelector("form").submit(); document.querySelector("form").dispatchEvent(new Event("submit"));
.submit() 不会触发 submission event, 它会直接发送到服务端.
相反, dispatch 只会触发 submit event, 而不会发送到服务端 (不管有没有 prevent default).
所以 2 个的用法是不同的哦, 要依据场景来运用. 一般上 Ajax Form 会用 dispatchEvent, 刷新 form 会用 .submit().
Ajax Form
所谓 Ajax Form 就是替代原本的 form submit 刷新体验, 改成通过 Ajax 发送 form 的信息.
XMLHttpRequest
这是平常用来发 Ajax 的方式, Content-Type: application/json
var request = new XMLHttpRequest(); request.onreadystatechange = () => { if (request.readyState == 4 && request.status == 200) { alert("done"); } }; request.open("POST", "/api/create-enquiry"); request.setRequestHeader("Content-Type", "application/json"); const dataJson = JSON.stringify({ username: "Derrick" }); request.send(dataJson);
ASP.NET Core
public class CreateEnquiryData { public string Username { get; set; } = ""; } public class EnquiryController { [HttpPost("api/create-enquiry")] public void CreateEnquiry([FromBody] CreateEnquiryData createEnquiryData) { } }
form 的话, 它不是 JSON
request.open("POST", "/api/create-enquiry"); const searchParams = new URLSearchParams({ username: "Derrick" }); request.send(searchParams);
直接发送 URLSearchParams 相等于是 application/x-www-form-urlencoded.
multipart/form-data 则使用是发送 FormData
request.open("POST", "/api/create-enquiry"); const formData = new FormData(); formData.append("username", "Derrick"); request.send(formData);
注:
1.在发送 URLSearchParams 和 FormData 时, 不要去设置 Content-Type Header, 游览器会依据类型自动设定好. 诺我们自己去设置反而会破坏掉游览器的机制, 比如 FormData 会自动创建分隔符, 如果手动设置 Content-Type 则不会.
2.JSON 则需要手动设置 Content-Type, 不然会是 text/plain.
3. 小总结