美文网首页
Fetch API与POST请求参数格式那些事

Fetch API与POST请求参数格式那些事

作者: Gerry2020 | 来源:发表于2020-03-07 12:49 被阅读0次

    一文读懂Fetch API与POST请求的四种参数格式

    简述

    相信不少前端开发童鞋与后端联调接口时,都会碰到前端明明已经传了参数,后端童鞋却说没有收到,尤其是post请求,遇到的非常多。本文以node.js作为服务端语言,借用express框架,简要分析客户端发送post请求的四种方式以及服务端如何接收。本文客户端请求没有借助第三方ajax库,采用的是Fetch API,虽然浏览器兼容性有点问题,但是用法简洁灵活,以后可能会是一个趋势。在说post请求之前,先简要概述下Fetch API

    Fetch API

    Fetch API提供了一个获取资源的接口(包括跨域请求),提供了更强大和灵活的功能集。未来可能是XMLHttpRequest的一种替代方案。去年GitHub代码去jQuery重构时,就使用Fetch API替代jQueryajax,毕竟目前JavaScript很多原生语法都进行了大量精简,比如DOM操作APIhttp请求fetches6+等。今天的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 = document.getElementById('btn')
        // 点击按钮,中止请求
        btn.addEventListener('click', e => {
            controller.abort()
        })
        // json方式提交数据
        const url = 'http://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 = response.text()
            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-Typeapplication/json;charset=utf-8,其中charset为采用的字符集。

    注意点:

    1. 既然为JSON提交,就要对参数进行序列化,即JSON.stringify(params),否则传递到服务端的参数可能是[Object object]
    2. 服务端(node.js)是以流的方式进行接收,接收完是一个JSON字符串,调用JSON.parse(params)可以对参数进行序列化

    示例代码


    客户端:

    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)
        })
    })
    

    服务端:

    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无需设置

    示例代码


    客户端:

    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 HeadersContent-Typeapplication/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 = ''
        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-Typemultipart/formdata

    示例代码


    客户端:

    <template>
        <input type="file" id="uploadFile">
    </template>
    <script>
    let $input = document.getElementById('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 = response.text()
            result.then(res => {
                console.log(res)
            })
        })
    }
    </script>
    

    服务端:

    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('<div id="path">'+path+'</div>');
        })
    })
    

    小结

    post请求向服务端提交参数,一般情况下都是放在body中,但是从上文列举的几种传参方式,仍然可以放在请求头中传递,服务端对于在请求头中传递的参数的处理和get请求保持一致。此外,从node.js接收的参数来看,除了放在请求头中能够直接获取外,其余三种请求方式都是以字节流的方式传递到服务端的。熟悉post请求的几种传参方式,有助于我们和后端同学进行接口联调。

    参考资料

    相关文章

      网友评论

          本文标题:Fetch API与POST请求参数格式那些事

          本文链接:https://www.haomeiwen.com/subject/hhoqdhtx.html