FetchAPI与POST请求参数格式那些事
简述
相信不少前端开发童鞋与后端联调接⼝时,都会碰到前端明明已经传了参数,后端童鞋却说没有收到,尤其是post请求,遇到的⾮常多。本⽂以node.js作为服务端语⾔,借⽤express框架,简要分析客户端发送post请求的四种⽅式以及服务端如何接收。本⽂客户端请求没有借助第三⽅ajax库,采⽤的是Fetch API,虽然浏览器兼容性有点问题,但是⽤法简洁灵活,以后可能会是⼀个趋势。在说post请求之前,先简要概述下Fetch API。
Fetch API
Fetch API提供了⼀个获取资源的接⼝(包括跨域请求),提供了更强⼤和灵活的功能集。未来可能是XMLHttpRequest的⼀种替代⽅案。去年GitHub代码
ajax实例 文件浏览去jQuery重构时,就使⽤Fetch API替代jQuery的ajax,毕竟⽬前JavaScript很多原⽣语法都进⾏了⼤量精简,⽐如DOM操作API、http请求fetch、es6+等。今天的axios可能就是明⽇的jQuery!
简单的实例
Fetch API主要暴露了三个接⼝⼀个⽅法。
三个接⼝
Request(资源请求)
Response(请求的响应)
Headers(Request/Response头部信息)
⼀个⽅法
fetch()(获取资源调⽤的⽅法)
// 实例化⼀个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请求,⽰例代码如下:
<template>
<button id='btn'>中⽌请求</button>
</template>
<script>
// 实例化controller
var controller = new AbortController()
// 获取实例的signal接⼝
var signal = controller.signal
let btn = ElementById('btn')
// 点击按钮,中⽌请求
btn.addEventListener('click', e => {
controller.abort()
})
// json⽅式提交数据
const url = '192.168.43.216:3000'
// 将signal接⼝放到请求配置项中
let testRequest = new Request(url + '/test', {
method: 'post',
headers: {
'Content-Type': 'application/json;charset=utf-8;'
},
body: '{"foo":"bar"}',
signal
})
fetch(testRequest).then(response => {
let result = ()
result.then(res => {
console.log(res)
})
})
</script>
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)可以对参数进⾏序列化
⽰例代码
客户端:
const url = '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 = ()
result.then(res => {
console.log(res)
})
})
服务端:
router.post('/test', (req, res, next) => {
let data = ''
<('data', chunk => {
data += chunk
})
<('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⽆需设置
⽰例代码
客户端:
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)
})
})
服务端:
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提交相同,参数格式与请求头提交⼀致
⽰例代码
客户端:
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)
})
})
服务端:
const fs = require('fs')
router.post('/form', (req, res, next) => {
let data = ''
<('data', chunk => {
data += chunk
})
<('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
⽰例代码
客户端:
<template>
<input type="file" id="uploadFile">
</template>
<script>
let $input = ElementById('uploadFile')
// 监听⽂件上传
$input.addEventListener('change', e => {
let file = e.target.files[0]
handleUploadFile(file)
})
function handleUploadFile (file) {
let bean = new FormData()
bean.append('file', file)
bean.append('hello', 'world')
let uploadFileRequest = new Request(`${url}/upload`, {
method: 'post',
headers: {
'Content-Type': 'multipart/formdata'
},
body: bean
})
fetch(uploadFileRequest).then(response => {
let result = ()
result.then(res => {
console.log(res)
})
})
}
</script>
服务端:
router.post('/upload', (req, res, next) => {
let data = []
let size = 0
<('data', chunk => {
data.push(chunk)
size += chunk.length
})
let rems = []
<('end', () => {
let buffer = at(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'});
})
})
请求信息:
⼩结
post请求向服务端提交参数,⼀般情况下都是放在body中,但是从上⽂列举的⼏种传参⽅式,仍然可以放在请求头中传递,服务端对于在请求头中传递的参数的处理和get请求保持⼀致。此外,从node.js接收的参数来看,除了放在请求头中能够直接获取外,其余三种请求⽅式都是以字节流的⽅式传递到服务端的。熟悉post请求的⼏种传参⽅式,有助于我们和后端同学进⾏接⼝联调。
参考资料
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论