使用 POST 请求


HTTP 定义POST来发送数据给服务器,POST适用于非等幂操作,若请求有副作用,多次POST请求的结果可以不同,它并非安全操作,可以用在修改数据库的内容,或在服务器上存储文件等。

如果要发送POST,则可以在非同步对象open时,将第一个参数设为'POST',在这「之后」使用setRequestHeader设定内容类型,这是因为POST要发送的数据会放在请求的本体中,必须告知发送的数据类型为何,接着在send时,将要发送的数据,作为send的实参传入。

例如,若发送表单类型数据,必须设置请求标头'Content-Type''application/x-www-form-urlencoded',以下是个示范:

...
let url = 'somewhere';
let queryString = 'a=10&b=20';
xmlHttp.open('POST', url);
xmlHttp.setRequestHeader('Content-Type',  'application/x-www-form-urlencoded');
xmlHttp.send(queryString);

放在 POST 本体中的数据,也有可能是其他格式,例如 XML 或 JSON,你必须设定不同的请求标头,这在之后还会说明。

在下面这个例子中,将先前对XMLHttpRequest的基本操作进行了简单的封装,并使用POST来实现〈使用 GET 请求〉 中第二个范例:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>

    新增书签:<br>
    网址:<input id="url" type="text">
    <span id="message" style="color:red"></span><br>
    名称:<input type="text">

<script type="text/javascript">    

    // 组合与编码请求参数
    function params(paraObj) {
        return Object.keys(paraObj)
                     .map(name => {
                         let paraName = encodeURIComponent(name);
                         let paraValue = encodeURIComponent(paraObj[name]);                         
                         return `${paraName}=${paraValue}`.replace(/%20/g, '+');
                     })
                     .join('&');
    }

    // 对 XMLHttpRequest 做简单封装
    class XHR {
        constructor() {
            let xhr = new XMLHttpRequest();

            // 你还可以加入其他处理器的设置…
            // 像是 loadstart、progress、abort、error、timeout、loadend 等
            let handlers = {
                'readystatechange' : new Set(),
                'load' : new Set()
            };

            xhr.onreadystatechange = function(evt) {
                handlers['readystatechange']
                    .forEach(handler => handler.call(xhr, evt));
            };

            xhr.onload = function(evt) {
                handlers['load']
                    .forEach(handler => handler.call(xhr, evt));
            };

            this.xhr = xhr;
            this.handlers = handlers;
        }

        addEvt(evtType, handler) {
            this.handlers[evtType].add(handler);
            return this;
        }

        removeEvt(evtType, handler) {
            this.handlers[evtType].delete(handler);
            return this;
        }

        open(method, url, paraObj, async = true, username = null, password = null) {
            let openUrl = paraObj ? `${url}?${params(paraObj)}` : url; 
            this.xhr.open(method, openUrl, async, username, password);
            return this;
        }

        addHeaders(headers) {
            Object.keys(headers)
                  .forEach(name => this.xhr.setRequestHeader(name, headers[name]));
            return this;
        }

        send(body = null) {
            this.xhr.send(body);
            return this;
        }
    }

    document.getElementById('url').onblur = function() {
        let reqString = params({ 
            url : document.getElementById('url').value 
        });

        let xhr = new XHR();
        xhr.addEvt('load', evt => {
            let req = evt.target;
            if(req.status === 200 && req.responseText === 'existed') {
                document.getElementById('message').innerHTML = 'URL 已存在';
            }            
        })
        .open('POST', 'POST-1.php')
        .addHeaders({'Content-Type' : 'application/x-www-form-urlencoded'})
        .send(reqString);
    };

</script>

</body>
</html>

按我观看结果

在事件的处理上,设计了addEvt可以加入处理器,以便与先前文件中看到的事件处理封装具有相同的风格。


展开阅读全文