You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.
<script>
let app = new Vue({el: '#form',data:{f1:'',f2:'',f3:'f3f3f3',f4:''},methods: {post(e){e.preventDefault()letform=this.$refs.formletformData=newFormData(form)letxhr=newXMLHttpRequest()xhr.open('POST','../network/show_post_data.php')xhr.send(formData)}}})
</script>
在日常前后端联调中,后端总会向前端提供一个接口文档,使得前端可以通过接口向后端拿数据。但前端到底要如何向后端传值才能够完成预期需求呢?本文就来简单介绍介绍。
从表单元素说起
首先我们来看一看HTML原生的表单元素
<form>
。在以往AJAX还未诞生前,若要向服务器提交数据,必然要使用这一元素。我们可以在为
<form>
标签设置一些属性:提交目标
提交时的请求方式,可选值为GET和POST
表单编码类型,可选值包括
application/x-www-form-urlencoded
和multipart/form-data
提交目标
一个URL,表示一个用于处理表单提交内容的接口,由后端提供。在该接口可对前端传递的内容进行各种操作,例如插入数据到数据库、存储文件到服务器的文件系统等等。本文实例使用一个PHP脚本来作为接口,该文件用于展示前端向服务器提交了什么内容,相关代码大致如下:
请求方法
HTML原生的请求方法包括GET和POST两种(DELETE、PUT、PATCH、OPTIONS等其他请求方法本文暂不展开),具体区别详见HTTP 方法:GET 对比 POST。
一般来说,GET方法用于获取数据(例如在日常通过浏览器访问网页的时候,就是通过GET方法拿到网页文件的),POST方法用于改变数据(例如文件的上传就是通过POST方法来进行的)。
编码类型
编码类型指的是表单最终将会以什么格式提交到服务器端。HTML原生表单支持的编码类型也就是上文中所说的
application/x-www-form-urlencoded
和multipart/form-data
。值得注意的是,GET 和 POST 两种请求方法所能够使用的编码类型并不相同:
那为何使用GET请求的表单不能够使用
multipart/form-data
来编码呢?详见下文。application/x-www-form-urlencoded
这种编码方式实际上在我们网上冲浪时便在使用 —— 虽然有时候并不是专用于表单。例如打开Github,我们在左上角搜索框(其实就是一个使用GET方法的表单)中输入
gogoend
来查找笔者在GitHub的一些活动,按下回车提交后,在搜索结果页面的 URL 上可以看到这样的字符串:?q=gogoend
,这就是application/x-www-form-urlencoded
编码,可在右侧开发者工具中看到字符串被解析后的结果。这种编码方式,以
?
分隔URL和参数,参数形如key=value
,多个参数之间以&
进行分割,表单中的空格替换为+
,表单中的非ASCII字符及特殊字符则被替换为URL编码 —— 所谓urlencoded
,可以认为这种编码方式是把表单内容转换为符合URL参数规范的字符串。例如,使用搜索框搜索
gogoend 杰杰大帅帅
,搜索结果页面URL中的搜索参数变成了:?q=gogoend+%E6%9D%B0%E6%9D%B0%E5%A4%A7%E5%B8%85%E5%B8%85
。事实上,这种编码类型是
<form>
默认的表单编码类型,无论该表单的请求方法是POST还是GET。例如有如下表单,请求方式是POST,我们没有为它设置
enctype
属性:提交后,在开发者工具网络面板下,找到提交发起的对应请求,在右侧窗格最下方Form Data即可看到我们提交的内容,点击view source即可看到提交内容的源码。同时可以看到上方请求头中的
Content-Type
为application/x-www-form-urlencoded
。如图所示:倘若我们将
f1字段
改为接受一个文件,此时再提交又会发生什么呢?经过测试,我们可以发现,此时只提交了该文件的文件名,文件内容并没有被提交……
如图:
那我们应该如何提交(上传)这个文件呢?这时,我们就需要把表单元素的enctype设置为
multipart/form-data
了。multipart/form-data
从字面意思理解,这里的表单数据是由各个部分组成的 —— 实际上也确实是这样。这种表单的编码类型中可以接受各种类型的字段,例如文本、二进制内容等等,无需像
application/x-www-form-urlencoded
那样经过URL编码,只需将内容原样插入,即可提交到服务器端。我们修改一下上文中做的一个表单的代码,将表单enctype属性强制设为
multipart/form-data
,然后对表单数据进行提交。代码如下:提交后发出的请求如图所示:
同样我们在网络面板中找到提交请求,查看Form Data中提交内容的源码,可以看到这样一串字符:
------WebKitFormBoundaryZkwLEvTG1tHjXbhl
同时我们可以发现上方请求头中的
Content-Type
为multipart/form-data; boundary=----WebKitFormBoundaryZkwLEvTG1tHjXbhl
在这里,这一串字符表示的是表单数据的分隔符,可以看出是随机产生的一个字符串,该字符串的内容写在
Content-Type
中,该字符串使得服务器端程序能够将表单中各个部分进行区分。我们可以多加入几个不同类型的字段来进行测试。
在表单中输入一些内容后,进行提交。下图右侧开发者工具中就展示了我们向服务器端提交的所有数据。
(略有尴尬……在Chrome浏览器中测试时,在表单提交后,并没有在开发者工具网络面板对应请求中找到Form Data —— 可能是因为笔者并没有使用XHR来提交,导致页面发生了跳转……但表单数据的确是提交成功的;为了方便截图,便使用了Firefox。)
来看看上文中所提及的问题:为何GET方法提交的表单不支持
multipart/form-data
编码?参考自GET - HTTP | MDN、RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content,GET没有请求体。
HTML中规范中有提到:
大意是:
在进行GET请求时,所有的请求参数都包含在URL中,且使用了URL编码;
multipart/form-data
中可以接受文本、二进制等内容,但multipart/form-data
仅可以通过请求体来传输。没有请求体的情况下,这些内容无法传输(当然或许也可以在URL中传输经过URL编码的二进制内容 —— 如果二进制内容很大,URL长度不敢想象)通过上文,我们了解了通过
向服务器端提交的表单的数据结构。对于PHP来说,无论表单采用何种编码类型,如果表单数据是使用POST方法传递的,则通过$_POST对象来取值,若表单数据包含文件,则在$_FILES中取值;如果表单数据是使用GET方法来传递的,则通过$_GET来取值。
进入Ajax时代
Ajax时代到来后,网页的用户体验提高了许多 —— 不必再通过来跳转页面进行请求了,可直接在当前页面使用XMLHttpRequest发起请求,使得前后端发生了分离。同时在请求体中可传递的内容也变得更加丰富,例如可传递JSON字符串等等。
目前在前端技术中,十分流行对表单数据和JavaScript中的相关对象进行双向绑定,比较有代表性的框架便是Vue.js。事实上在笔者日常所做的业务中,对于HTML原生表单的使用很少,向后端传值几乎都是使用JSON字符串,原生表单的使用场景仅限于上传文件等极少数情况。
例如,下方的代码描述的是一个使用JSON来作为表单数据进行传递的例子:
在这里,
<form>
基本已经失去了它原先的作用,原生的特性只使用了提交事件的监听,而且我们还阻止了它的默认提交行为。在这里使用Vue实例上的data
对象来收集数据,提交时将这一对象转换为JSON字符串,并将该字符串在XHR实例发送数据时作为请求体传入send方法。我们看一下提交后发送出去的请求,如图所示:
提交给服务器后,服务器端便可以对提交的JSON字符串进行解析。PHP
var_dump()
输出的内容如下:在表单中,我们选择了一个文件来进行上传;无奈在提交前对表单数据进行
JSON.stringify()
操作时,我们选择好的文件变成了一个空对象。其他数据都成功传递到了服务器,我们选择的文件并没有上传到服务器。因此,虽然目前十分流行使用JSON来对数据进行传输,但对于文件上传便显得有些无能为力了。或许可以在JSON中内嵌文件的base64编码来上传文件,但经过base64编码文件体积会膨胀……这时候,我们就只好请来我们的老朋友 ——multipart/form-data
。在这里,如果使用原生的上传方式,在上传结束后页面会发生跳转,这样来看用户体验不是很好,那如何通过Ajax来传递multipart/form-data
?事实上,浏览器中提供了一个名为FormData的类来用于模拟一个使用
multipart/form-data
编码的表单。在这里我们需要把之前的表单数据转换为FormData。FormData实例通过new来构建,构造函数中可接受一个表单元素作为参数。我们对上方提交部分的代码稍作修改:这里的FormData实例中的数据来自于元素中已输入的值。将FormData实例在XHR实例发送数据时作为请求体传入send方法,即可将模拟表单中的数据传递给后端。如图:
传输的FormData的源码和原生表单的
multipart/form-data
编码后的源码基本相同。有关FormData的更多内容,见有关 HTML 表单的一些事儿 —— FormData 篇。当然,这里仅仅是一个模拟场景,对于如何上传文件还是应当与后端进行联调。例如,后端可能此处会分为两个接口进行两步操作 —— 一个用于上传文件,另一个用于提交表单。
使用Postman调用接口
Postman是一款十分常用的接口调试工具。
调试GET接口
通常在 Params 选项卡中输入参数,最终参数将会被URL编码后拼接到URL后方。
调试POST接口
通常在 Body 选项卡中输入参数,最终参数将会被加入请求体。我们可以在 Body 选项卡下看到几个单选项目 —— form-data、x-www-form-urlencoded、raw、binary。
这里我们根据向接口传递的数据类型,选择具体的选项。
multipart/form-data
;application/x-www-form-urlencoded
;The text was updated successfully, but these errors were encountered: