web浏览器、服务器和相应的web应用程序都是通过HTTP协议相互通信的。HTTP使用的是可靠的数据传输协议,它能够确保数据在传输的过程中不会被损坏或产生混乱。Http请求方式有多重,最近遇到了关于post的一些问题,所以这里专注了解下post提交数据的几种方式。
HTTP应用程序发送的数据块就是HTTP报文,它以一些文本形式的元信息
开头,这些元信息描述了报文的内容及含义。后面跟着可选的数据部分。
所有HTTP的报文可以分为两类:请求报文(request message)和响应报文(response message)。请求报文会向web服务器请求一个动作,响应报文会将请求的结果返回给客户端。请求报文和响应报文的基本报文结构相同。
请求报文结构:
1 | <method><request-URL> <version> |
响应报文结构:
1 | <version> <status> <reson-phrase> |
HTTP协议规定POST提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么样的编码方式,所以开发者可以自主定义消息体的格式,只要最后发送的HTTP请求满足上面的格式就可以。
但是,数据发送出去后,还需要服务端可以解析成功才有意义。一般服务端的语言和框架都可以正常解析常见的数据格式。服务端通常是根据请求头(headers)中的Content-Type
字段来获得请求中的消息主题是用何种方式编码,再对主题进行解析。所以POST提交数据的方式,主要是Content-Type
和消息主体编码的方式。
application/json
json的请求头基本上已经很常见了。它用来告诉服务端消息主体是序列后的json字符串。由于JSON规范的流行,除了低版本的IE之外各大浏览器都原生支持JSON.stringify
,服务端语言也都有处理JSON的函数。一些框架都已经默认支持了。
它的好处是,可以直接用json传输结构化的数据,相比其他只能键值对的传输方式,这一点非常有用。顺便说一句,现在Angular里面默认支持的传输方式直接是application/json
,可以直接使用。例如:
1 | constructor( |
直接调用httpClient来发起一个请求,直接使用json传递参数,服务端可以直接获取json数据。
application/x-www-form-urlencoded
浏览器的原生<form>
表单,如果不设置enctype
属性,那么最终就会以application/x-www-form-urlencoded
方式提交数据。请求类似下面这样:
1 | Content-Type: application/x-www-form-urlencoded;charset=utf-8 |
这个提价方式,参数会以键值对的方式进行传输:key=value&key=value
,另外,提交的数据的key和value都进行了url转码。大部分服务端语言都对这种方式有很好的支持。
那在angular中如何将表单的提交方式转换到这种提交呢?毕竟我们使用的HttpClient
是用的fetch
提交,而不是原生表单的提交方式。
首先,我们需要将post请求方的header报文里面的Content-Type
字段设置为application/x-www-form-urlencoded
:
1 | constructor( |
在浏览器开发工具的NetWork里面查看的时候,会发现这个请求的Content-Type
已经变成了application/x-www-form-urlencoded
,参数那一块内容也变成了form-data
。但是,你在服务器里面处理这个请求的时候,没法子接收到参数值的。为什么?因为虽然我们把请求方式变了,但是参数还是json。这里需要对参数也处理一下:
1 | const params = {name: 'tony', age: 18}; |
这样,服务器里面可以接收到参数name=tony&age=18
,然后就可以正常解析了。
但是,这样处理是很不方便的,在一个大应用里面,总不能每个请求都这样设置。建议写一个公共方法去处理参数格式化,然后在拦截器里面去处理header,这样可以方便的扩展:
1 | intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { |
multipart/form-data
当使用表单上传文件时,必须让<form
表单的enctype
等于multipart/form-data
,看实例:
1 | POST http://www.example.com HTTP/1.1 |
首先生成了一个个boundary(边界)用于分割不同的字段,为了避免与正文内容重复,boundary很长很复杂。然后Content-Type
指明了数据是以multipart-data
来编码,本次请求的boundary内容是什么。
这种方式一般用来上传文件。
enctype
还支持text/plain
,但用的非常少。
text/xml
xml的方式传递参数,太麻烦了,不常用,还是用json吧。哈哈哈哈。。。。😄
其实是最近在开发angular项目的时候,忽然遇到了使用form-data
方式提交表单的问题,本来摸索摸索可以提交了,但是当我提交一个比较复杂的字符串的时候服务器老是接收不到,后面才发现是没有进行url编码,所以后端直接爆炸了。也是很囧,正好借助这个机会来梳理一下,同时也是个记录。
参考文章:https://imququ.com/post/four-ways-to-post-data-in-http.html