Fetch API与POST请求那些事
Gerryli 人气:0
## 简述
相信不少前端开发童鞋与后端联调接口时,都会碰到前端明明已经传了参数,后端童鞋却说没有收到,尤其是`post`请求,遇到的非常多。本文以`node.js`作为服务端语言,借用`express`框架,简要分析客户端发送`post`请求的四种方式以及服务端如何接收。本文客户端请求没有借助第三方`ajax`库,采用的是`Fetch API`,虽然浏览器兼容性有点问题,但是用法简洁灵活,以后可能会是一个趋势。在说`post`请求之前,先简要概述下`Fetch API`。
## Fetch API
`Fetch API`提供了一个获取资源的接口(包括跨域请求),提供了更强大和灵活的功能集。未来可能是`XMLHttpRequest`的一种替代方案。去年`GitHub`代码去`jQuery`重构时,就使用`Fetch API`替代`jQuery`的`ajax`,毕竟目前`JavaScript`很多原生语法都进行了大量精简,比如`DOM`操作`API`、`http`请求`fetch`、`es6+`等。今天的`axios`可能就是明日的`jQuery`!
### 简单的实例
`Fetch API`主要暴露了三个接口一个方法。
***
- 三个接口
- `Request`(资源请求)
- `Response`(请求的响应)
- `Headers`(`Request/Response`头部信息)
- 一个方法
- `fetch()`(获取资源调用的方法)
```javascript
// 实例化一个Request实例
// 第一个参数一般指资源路径
// 第二个参数可以理解为请求的配置项,包含头部信息和http请求一些关键配置(请求类型、参数...)
let requestInstance = new Request('/hello', {
method: 'post',
headers: {
'Content-Type': 'application/json;charset=utf-8'
},
body: '{"hello": "world"}'
})
// fetch方法参数同Request实例
// 第一个参数为url或者Request实例
// 第二个参数为请求配置项
fetch(requestInstance).then(response => {
// 返回的是一个Response的实例
// 调用Response实例的序列化方法,序列化成json,返回值是一个promise
// 序列化方法有 json,text,formData,blob,arrayBuffer,redirct
let result = response.json()
result.then(res => {
consolee.log(res)
})
})
```
### 有意思的特性
`Fetch API`添加了一个实验性的功能,支持客户端手动取消`http`请求了,这个比较有意思,因为之前的`ajax`貌似都不支持手动取消。借助`AbortSignal`接口,可以通过`AbortController`实例化一个控制器,将实例的`siginal`当做请求的配置项,传递到服务端,客户端可以通过`AbortController`实例的`abort`方法,来终止当前的`http`请求,示例代码如下:
```html
```
## post请求四种传参方式
本文所说的前端传递数据格式相对于主流的`ajax`函数库有一定区别,一般的`ajax`函数库为了方便用户使用,都会对数据进行二次封装。本文主要说**原始的数据格式交互**,具体`ajax`库的使用,还是以官方文档为准。
请求头(`Request Headers`)的实体`Content-Type`用于指示资源的`MIME`类型,即客户端传递消息的格式;响应头中`Content-Type`用于指示服务端返回消息的格式。所以在`http`请求中,我们可以从报文中的`Content-Type`属性来判断客户端-服务端消息传递的格式。
### JSON提交
`JSON`是常用的一种前后端数据接收格式。前端传递的是键值对数据,即对象(`Object`)。采用`JSON`传递参数,请求头`Content-Type`为`application/json;charset=utf-8`,其中`charset`为采用的字符集。
**注意点:**
1. 既然为JSON提交,就要对参数进行序列化,即`JSON.stringify(params)`,否则传递到服务端的参数可能是`[Object object]`
2. 服务端(`node.js`)是以流的方式进行接收,接收完是一个`JSON`字符串,调用`JSON.parse(params)`可以对参数进行序列化
**示例代码**
***
客户端:
```javascript
const url = 'http://192.168.43.216:3000'
let testRequest = new Request(url + '/test', {
method: 'post',
headers: {
'Content-Type': 'application/json;charset=utf-8;'
},
body: JSON.stringify({a: 1})
})
fetch(testRequest).then(response => {
let result = response.text()
result.then(res => {
console.log(res)
})
})
```
***
服务端:
```javascript
router.post('/test', (req, res, next) => {
let data = ''
req.on('data', chunk => {
data += chunk
})
req.on('end', () => {
// 将JSON字符串解析成对象
data = JSON.parse(data)
res.send(data)
})
})
```
***
**请求信息:**

### 请求头提交
在实际开发中,遇到过不少后端开发,喜欢吧请求参数放在请求头,类似于`get`请求,即请求的参数是拼接在请求地址后面。个人觉得这种传参方式并不好,一般浏览器对`URL`长度是有限制的,以`Chrome`为例,`URL`最大长度正在`7700`个字符左右,对于`post`请求来说,最好参数还是放在`body`中。
**注意点**
1. 客户端请求参数拼接在`url`后,在`?`后,键值对写法`a=1`,多个键值对之间通过连接符`&`连接
2. 服务端能够在`request`对象中,通过`request.query`直接进行接收
3. 由于参数是拼接在`url`后面,所以请求头`Content-Type`无需设置
**示例代码**
***
客户端:
```javascript
let queryStringRequest = new Request(`${url}/querystring?a=1&b=2`, {
method: 'post'
})
fetch(queryStringRequest).then(response => {
let result = response.json()
result.then(res => {
console.log(res)
})
})
```
***
服务端:
```javascript
router.post('/querystring', (req, res, next) => {
res.send(req.query)
})
```
***
**请求信息:**

### 普通表单提交
表单提交的方式有两种,一种是普通的表单提交,另外一种是通过`FormData`进行提交(主要应用在文件上传)。单纯的表单提交,与上述两种参数格式上还是存在一定的差别的,主要体现在以下几个方面。
1. `Content-Type`
表单提交`Request Headers`的`Content-Type`为`application/x-www-form-urlencoded;charset=utf-8`。
2. 参数
表单提交参数是放在`body`中,感觉是`JSON`和请求头提交的合体。参数位置与`JSON`提交相同,参数格式与请求头提交一致
**示例代码**
***
客户端:
```javascript
let formRequest = new Request(url + '/form', {
method: 'post',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
},
body: 'a=1&b=2'
})
fetch(formRequest).then(response => {
let result = response.json()
result.then(res => {
console.log(res)
})
})
```
***
服务端:
```javascript
const fs = require('fs')
router.post('/form', (req, res, next) => {
let data = ''
req.on('data', chunk => {
data += chunk
})
req.on('end', () => {
data = decodeURI(data)
// 将a=1&b=2解析成{a: 1, b: 2}
let dataObj = querystring.parse(data)
res.send(dataObj)
})
})
```
***
**请求信息:**

### FormData提交 (文件上传)
通常我们在进行文件上传时,都会采用表单提交。参数放在`body`中,只不过格式与普通的有差别,具体如下:
1. 参数需要放在`FormData`的实例中,通过`append`进行参数的添加
2. 请求头`Content-Type`为`multipart/formdata`
**示例代码**
***
客户端:
```html
```
***
服务端:
```javascript
router.post('/upload', (req, res, next) => {
let data = []
let size = 0
req.on('data', chunk => {
data.push(chunk)
size += chunk.length
})
let rems = []
req.on('end', () => {
let buffer = Buffer.concat(data, size)
for (let i = 0; i < buffer.length; i++) {
var v = buffer[i];
var v2 = buffer[i+1];
if(v==13 && v2==10){
rems.push(i);
}
}
// 图片信息
var picmsg_1 = buffer.slice(rems[0]+2,rems[1]).toString();
var filename = picmsg_1.match(/filename=".*"/g)[0].split('"')[1];
// 图片数据
var nbuf = buffer.slice(rems[3]+2,rems[rems.length-2]);
var path = './static/'+filename;
fs.writeFileSync(path , nbuf);
res.writeHead(200, { 'Content-Type': 'text/html;charset=utf-8'});
res.end('
'+path+'
');
})
})
```
***
**请求信息:**

## 小结
`post`请求向服务端提交参数,一般情况下都是放在`body`中,但是从上文列举的几种传参方式,仍然可以放在请求头中传递,服务端对于在请求头中传递的参数的处理和`get`请求保持一致。此外,从`node.js`接收的参数来看,除了放在请求头中能够直接获取外,其余三种请求方式都是以字节流的方式传递到服务端的。熟悉`post`请求的几种传参方式,有助于我们和后端同学进行接口联调。
## 参考资料
- [fetch](https:/https://img.qb5200.com/download-x/developer.mozilla.org/zh-CNhttps://img.qb5200.com/download-x/docs/Web/API/Fetch_API)
- [Request](https:/https://img.qb5200.com/download-x/developer.mozilla.org/zh-CNhttps://img.qb5200.com/download-x/docs/Web/API/Request)
- [Response](https:/https://img.qb5200.com/download-x/developer.mozilla.org/zh-CNhttps://img.qb5200.com/download-x/docs/Web/API/Response)
- [AbortSignal](https:/https://img.qb5200.com/download-x/developer.mozilla.org/zh-CNhttps://img.qb5200.com/download-x/docs/Web/API/AbortSignal)
- [node.js文件上传](https://www.cnblogs.com/axes/p/4308430.html)加载全部内容