1. Http数据传参的两种方式
- uri传参
数据参数写在uri地址中,可以为查询参数,也可以为路径参数,一般get请求方式用的多。
优点:一个TCP传输就可以将参数传递给服务器,速度快。
缺点:参数直接暴露在浏览器中,不安全。 - body传参
数据参数写在body体中,有多种content-type格式:json、xml、form-data,一般post请求方式用的多。
优点:相对安全,没有大小限制。
缺点:需要两个TCP传输,第一次传输head,第二次传输body。
注意,上面说的post和get,并不是说post只能用body传参,get只能用uri传参,实际上,想怎么传参是我们码农自己定的,你可以在post中添加uri参数,也可以在get方式中添加body参数,只是这样做的话,不符合规范,而且很多服务器是按照规范来自定解析方法的,比如:有些服务器会帮你解析,读出body数据,而有些服务器直接忽略,所以,虽然GET可以带request body,但不能保证一定能被接收到。这里有个post和get的区别的文章:https://www.cnblogs.com/logsharing/p/8448446.html 写得很好。
2. Http请求方式
我们知道Restful下常用的几种http请求方式为get、post、put、patch、delete,我们首先要明确一点,http请求方式只是一种约定俗成的规范,因为只要后端对前端发来的get请求做了post该做的事,实际上就是get也完成了post的功能。
正是因为有了这种规范,所以后端开发人员在写不同的api接口时,就要注意自己的方法是否符合当前的http请求方式。比如注册用户这个api,是要创建一个用户到数据库的,这是一个非幂等操作,自然要使用post方式(实际上后端使用get方式也能实现,但是正因为有了这种规范就不推荐使用)。所以归根结底,前后端交互时的http的请求方式,是看我们后端在一定的规范约束之内怎么实现http操作方法,如何定义http路由,如何定义数据交互格式。
下面我们一一来看这几种请求方式。
- get 获取资源
- post 创建资源
- put 创建或更新资源(有则更新,无则创建)
例如 PUT /items/1 的意思是替换 /items/1 ,存在则替换,不存在则创建。
所以,PUT方法一般会用来更新一个已知资源。 - patch 部分更新资源
- delete 删除资源
3.conten-type的不同数据格式及使用注意事项
-
application/json (在axios中默认的post就是这种格式)
这个大家很熟悉,就是json格式的数据,在网络流中被编码成json格式的字符串,如:{“key1”:“value1”,“key2”:“value2”} -
application/x-www-form-urlencoded
浏览器的原生 form 表单格式,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码,最终网络流中被编码成类似于这种格式的字符串:title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3 -
multipart/form-data
也是浏览器的原生 form 表单格式,与上面不同的是,这个既可以传键值对,也可以用来上传文件。我们使用表单上传文件时,必须让 form 的 enctyped 等于这个值。
对于其具体格式,我们来看一下它的请求示例:POST http://www.example.com HTTP/1.1 Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="text" title ------WebKitFormBoundaryrGKCBY7qhFd3TrwA Content-Disposition: form-data; name="file"; filename="chrome.png" Content-Type: image/png PNG ... content of chrome.png ... ------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
实际上对于每一个boundary都是一个键值对,可以包含多种不同的数据类型,可以是text、png
、二进制文件流等。
所以在进行开发时,要弄清楚我们需要传输的数据到底是什么格式的,比如,如果传的是一个file文件,那么我们的content-type就要为multipart/form-data,如果content-type错误的写为application/json,必然会导致后端解析错误。
//文件上传表单
<form @submit.prevent="upload" method="post" enctype="multipart/form-data">
<input type="file" name="picture" v-on:change="onChange($event)">
<button type="submit">上传图片</button>
</form>
//axios处理
var vm = new Vue({
el: "#vm",
data: {
picture: {},
result: '',
},
methods: {
onChange: function (event) {
this.picture = event.target.files[0]; // get input file object
console.log(this.picture);
},
upload: function () {
var that = this;
var formData = new FormData();
formData.append('picture', this.picture);
// specify Content-Type, with formData as well
this.$http.post('/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' } //注意这里要修改请求头的Content-Type属性
}).then(function (res) {
res.json().then(function (result) {
that.result = result.info;
console.log(that.result);
});
}, function (res) {
console.log(res.body);
});
}
}
});
当然后端也要清楚传过来的是什么格式的数据,对于不同的数据,后端采用的接收数据的方法也不同。比如,如果前端传过来的是一个文件数据,你还是用普通的就收方式处理,那肯定会出错。
一般来说这些后端框架,肯定会有相应的内置文件接收解析对象(或者说是接口-如MultipartFile,暴露给开发人员自定义实现文件对象)。
举个例子,spring中,你需要使用@RequestParam() MultipartFile来接收文件对象,如果你还是使用@RequestBody来进行接收,肯定会出错。
4. 具体应用
先说一下我们网络中基本上传输的都是字符串类型,并不是我们以为的传的是个对象。就拿现今最流行的json来说,前后端在网络交互数据流中,接收到的对方数据并不是json对象,而是一个json格式的字符串,所以前后端都需要对json字符串解析,一般来说,我们会在请求响应的最前端进行拦截并处理。现在许多框架已经在其端点Filter中自动帮我们我们做了这一转换。
现在我们再看一下具体的后端处理前端传参的例子。
先要说明一下,我们要关注的是注解(.net中叫特性)和传参方式(uri传参、body传参)的对应关系,而不是和http请求方式的对应关系。因为http请求方式只是一个数据操作规范,对于这两种传参方式它都可以实现。
比如 .net 中:
- [FromBody]特性实现处理body体中的数据
- [FromUri]特性实现处理uri中的查询参数或者路径参数
其底层是使用媒体格式化程序(MediaTypeFormatter——异步 BufferedMediaTypeFormatter——同步)来接收并解析body中的数据。他们首先根据Content-type来判断所接受的数据类型(比如text/html、application/json等),然后可根据Accept中(text/html,application/xhtml+xml,application/xml)返回固定格式的数据。
再比如spring boot中:
-
uri参数
- @PathVariable
获取路径参数。即url/{id}。 - @RequestParam
获取查询参数。即url?name=panda。
@GetMapping("/demo/{id}") public void demo(@PathVariable(name = "id") String id, @RequestParam(name = "name") String name) { System.out.println("id="+id); System.out.println("name="+name); }
- @PathVariable
-
Body参数(context-type)
-
@RequestBody
获取并解析body中的application/json格式数据@PostMapping(path = "/demo") public void demo1(@RequestBody Person person) { System.out.println(person.toString()); }
-
无注解 或者 @RequestParam
获取并解析body中的multipart/form-data和x-www-form-urlencoded
multipart/form-data既可以用做文件数据的传输也可以做键值对的传输(一般做文件传输的时候都要使用这种格式)
x-www-form-urlencoded只能用做键值对@PostMapping(path = "/demo2") public void demo2(Person person) { System.out.println(person.toString()); }
SpringMVC通过使用HandlerAdapter 配置的HttpMessageConverters来解析HttpEntity中的数据,然后绑定到相应的bean上
-