Ajax(下)

405 阅读9分钟

GET请求

GET 请求一般用于信息获取,它没有请求主体,而是使用 URL 传递参数(即:传递数据给后台)。

传递参数的方式:

  1. 对所需发送的数据(具有名称和值)执行普通的 URL 编码,即:由一对对 "名称=值" 组成(称为:名/值对),每一对之间用 "&" 拼接,如 "name=value&name=value&...&name=value";
  2. 由于 名/值对 会附加在 URL 地址后面,因此在这串字符参数的最前面需要添加个 "?",表示 URL 的 查询参数 开始。
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Tryrun 10</title>
</head>
<body>
    <div id="form">
        <label for="country">国家:<input type="text" name="country" id="country"></label>
        <label for="city">城市:<input type="text" name="city" id="city"></label>
    </div>
    <hr>
    <div>你查询的国家是:<span id="ipt_country"></span></div>
    <div>你查询的城市是:<span id="ipt_city"></span></div>
    <br>
    <button type="button" id="search">查询</button>
    (查询成功后会把你输入的值显示在上方)

    <script>
        var oSearch = document.getElementById("search"),
            oIpt_country = document.getElementById("ipt_country"),
            oIpt_city = document.getElementById("ipt_city");

        var url = "/statics/demosource/demo_get_json.php";

        oSearch.onclick = function () {
            var country = document.getElementById("country").value,
                city = document.getElementById("city").value;

            var query = "country=" + country + "&city=" + city;

            var queryURL = url + "?" + query;

            // 发起 get 请求
            ajaxGet(queryURL);
        }

        function ajaxGet (url) {
            var xhr = window.XMLHttpRequest ? new window.XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
            xhr.onreadystatechange = function () {
                if (xhr.readyState === 4) {
                    if (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) {
                        var res = JSON.parse(xhr.responseText);
                        oIpt_country.innerText = res.params.country;
                        oIpt_city.innerText = res.params.city;
                    }
                }
            }
            
            xhr.open("GET", url);
            xhr.send();
        }
    </script>
</body>

缓存问题

对于 GET 请求,请求的结果会被浏览器缓存,特别是在 IE 浏览器下。这时,如果 GET 请求的 URL 不变,那么请求的结果就是浏览器的缓存(也就是上次 GET 请求的结果)。

解决办法

实时改变 GET 请求的 URL,只要 URL 不同,就不会取到浏览器的缓存结果。

在 URL 末尾添加时间戳参数。由于时间戳可以精确到毫秒,从而保证了每次发起 GET 请求的时间不同,达到实时改变请求 URL 的目的。

var url = "/statics/demosource/demo_get_json.php";

// 在请求参数的最后附加时间戳参数 t
var query = "user=" + user + "&pwd=" + pwd + "&t=" + new Date().getTime();

var queryURL = url + "?" + query;
// ajax_get为自己封装的请求对象,不是固定用法
ajax_get(queryURL);

封装 GET 异步请求函数

步骤:

  1. 实例化一个XMLHttpRequest对象,如果你正在使用 IE7 以下版本的浏览器,应该对它做兼容处理;

  2. data数据执行普通的 URL 编码,也可以使用预置代码中提供的urlencodeData工具函数完成这步操作;

  3. 调用open()方法,指定请求方式、请求地址、是否异步,注意请求地址需要是url与请求参数拼接的结果;

  4. 调用send()方法;

  5. XMLHttpRequest实例添加readystatechange事件处理程序:

    • 最好在open()方法调用之前实现readystatechange事件,比较严谨;
    • 在事件处理程序中判断 HTTP 请求状态,只有请求状态为 "完成" 时,才能保证响应内容完整;
  6. 根据 HTTP 状态码(即status属性值),执行对应的回调函数:

    • HTTP 状态码在 200 与 300 之间(不包括300)、或为 304 都表示成功:使用JSON.parse()方法将responseText属性值解析为 JavaScript 对象,并作为success函数的实参传出;
    • HTTP 状态码为其它值则表示请求失败:调用error函数,并将失败的 HTTP 状态码作为实参传出;
  7. 在运行结果区封装的 ajaxPost,能否正常响应,常见的错误有:

    • 成功请求的测试出现 403:GET 请求的 URL 地址不正确,可以检查一下是否在url与 请求参数之间拼接了 "?"。
<html>
<head>
    <meta charset="UTF-8">
    <script>
        function ajaxGet (url, data, success, error) {
            // 在下方开始你的代码
            var xhr = window.XMLHttpRequest ? new 				window.XMLHttpRequest():new ActiveXObject('Microsoft.XMLHTTP');
            data = urlencodeData (data)
            console.log(data);
            xhr.onreadystatechange = function(){
                if(xhr.readyState !== 4) return;
                if(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304){
                    console.log(xhr.status);
                    var res = JSON.parse(xhr.responseText)
                        success(res);
                }else{
                    var res = JSON.parse(xhr.responseText)
                    console.log(res);
                    error(res);
                }
               
                    
            };
            url = url + '?' + data;
            xhr.open('GET',url,true);
            xhr.send(null);
            }
         
    </script>
    
    
    <!-- 工具函数 -->
    <script>
        // 用于对 JavaScript 对象执行普通的 URL 编码
        // 编码后的格式为:"名称=值&...&名称=值"
        function urlencodeData (data) {
            if (!data) return;
            var pairs = [];
            for (var name in data) {
                if (!data.hasOwnProperty(name)) continue;
                if (typeof data[name] === "function") continue;
                var value = (data[name] === null || data[name] === undefined) ? "" : data[name].toString();
                pairs.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
            }
            return pairs.join("&");
        }
    </script>
    
    <!-- 以下均是测试代码 -->
    <!-- 测试代码的 css 部分 -->
    <style>
        #request { text-align: center; }
        .request-result { padding: 12px; border: 1px solid #e8e8e8; 			border-radius: 2px; box-shadow: 0 1px 3px 1px #d9dfe9; }
        .request-btn { margin-top: 12px; padding: 7px; color: #fff; 			border-radius: 7px; transition: all .2s; cursor: pointer; }
        .request-success { background-color: #1890ff; }
        .request-success:hover { background-color: #40a9ff; }
        .request-error { background-color: #d9363e; }
        .request-error:hover { background-color: #ff7875; }
    </style>
</head>
<body>
    <!-- 测试代码的 html 部分 -->
    <div id="request">
        <div class="request-result">
            <div class="res-tip">测试下你封装的异步 GET 请求能不能正常响</div>
            <div class="res-param"></div>
        </div>
        <div class="request-btn request-success">成功请求的测试</div>
        <div class="request-btn request-error">失败请求的测试</div>
    </div>
    
    <!-- 测试代码的 js 部分 -->
    <script>
        var oDivs = document.getElementsByTagName("div");
        var oResult_tip = oDivs[2],
            oResult_param = oDivs[3],
            oSuccess = oDivs[4],
            oError = oDivs[5];
            
        var url = "/statics/demosource/demo_get_json.php",
            badUrl = "/statics/demosource/404.txt";
        var data = {
                aa:null,
                from: "南昌",
                to: "厦门",
                time: "今天"
            };
        var success = function (res) {
            oResult_tip.innerText = "请求成功";
            oResult_param.innerHTML = "<div><span>起点:</span><span>" + res.params.from + "</span></div><div><span>终点:</span><span>" + res.params.to + "</span></div><div><span>时间:</span><span>" + res.params.time + "</span></div>";
        };
        var error = function (res) {
            oResult_tip.innerText = "请求失败:" + res;
            oResult_param.innerHTML = "";
        };
        
        oSuccess.onclick = function () {
            ajaxGet(url, data, success, error);
        };
        
        oError.onclick = function () {
            ajaxGet(badUrl, data, success, error);
        }
    </script>
</body>
</html>

POST请求

POST 请求一般用于修改服务器上的资源,它需要发送一个请求主体,客户端传递给服务器的数据就包含在这个请求主体中。

"Content-Type"请求头用于设置请求主体的编码格式。

表单编码的 POST 请求

POST 请求使用 表单编码 的方式来发送数据的关键步骤:

  1. 对所需发送的数据(具有名称和值)执行普通的 URL 编码,即:像 GET 请求那样拼接为 名/值 对的形式;
  2. "Content-Type"请求头的值设置为"application/x-www-form-urlencoded"
// 获取用户输入的表单数据
var country = document.getElementById("country").value,
    city = document.getElementById("city").value;

// 将数据拼接为 名/值对 的形式
var query = "country=" + country + "&city=" + city;

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
    // ... ... 省略事件处理程序
}

// 指定 POST 请求
xhr.open("POST", "/statics/demosource/demo_post_json.php");

// 设置请求主体的编码方法
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");

// 发送请求主体(数据)
xhr.send(query);

JSON 编码的 POST 请求

JSON 是一种轻量级的前后端数据交换格式,直接使用JSON.stringify原生 API 即可实现 JSON 编码,比表单编码的方式更加快捷。

POST 请求使用 JSON编码 的方式来发送数据的关键步骤:

  1. "Content-Type"请求头的值需要为"application/json"
  2. 对请求主体进行序列化,在 JavaScript 中可使用JSON.stringify完成这步操作。
// 获取用户输入的表单数据
var country = document.getElementById("country").value,
    city = document.getElementById("city").value;

// 将数据转换为 JavaScript 对象
var data = {
    country : country,
    city : city
}

var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
    // ... ... 省略事件处理程序
}

// 指定 POST 请求
xhr.open("PO ST", "/statics/demosource/demo_json_data.php");

// 设置请求主体的编码方法
xhr.setRequestHeader("Content-Type", "application/json");

// 编码请求主体并发送
xhr.send(JSON.stringify(data));

两种方式的比较

GET 请求:

  1. 一般用于信息获取:通过发送一个请求来取得服务器上的资源;
  2. 数据包含在 URL 地址中;
  3. 数据量受 URL 的长度限制;
  4. 不安全:浏览器的 URL 可见到,明文传输;
  5. GET 请求会被缓存
  6. GET 没有请求主体,请求速度相对较快。

POST 请求:

  1. 一般用于修改服务器上的资源:向指定资源提交数据,后端处理请求后往往会导致服务器 建立新的资源 或 修改已有资源;
  2. 数据包含在请求主体中;
  3. 没有数据量限制,可在服务器的配置里进行限制;
  4. 只能是比 GET 安全,实际上也是不安全的:可通过开发者工具或者抓包看到,明文传输;
  5. POST 请求不会缓存
  6. POST 相对稳定、可靠:可发送包含未知字符的内容。

容易产生的误区:HTTP 协议里并没有限制 GET 和 POST 的长度,GET 的最大长度限制是因为浏览器和 Web 服务器对 URL 的长度限制,不同的浏览器和 Web 服务器限制的最大长度不一样,它们所限制的是整个 URL 的长度,而不仅仅是查询参数的数据长度。

Ajax扩展

jQuery 中的 Ajax

jQuery 是一个 JavaScript 工具库,它封装了 JavaScript 常用的功能代码,包括我们刚刚学完的 Ajax。

jQuery 中,Ajax 常见的请求方式有以下几种:

  • $.ajax(url, options)
  • $.get(url, data, callback, dataType)
  • $.post(url, data, callback, dataType)
  • $.getJSON(url, data, callback)
  • $.getScript(url, callback)
  • jQuery元素.load(url, data, callback)
// 使用jQuery发起ajax请求
$.ajax("/statics/demosource/demo_get_json.php", {
    //请求类型
    type: "GET",
    //要发送的数据
    data: {
        country: country,
        city: city
    },
    //数据格式
    dataType: "json",
    //请求成功后执行
    success: function (res) {    // res为响应成功返回的数据
        oIpt_country.innerText = res.params.country;
        oIpt_city.innerText = res.params.city;
    },
    //请求失败后执行
    error: function (res) {    // 这里的res为响应失败返回的数据
        alert("请求失败:" + res.status);
    }
});

Ajax 的替代品:fetch

Fetch API 是随 ES6 发展而出现的一个 JavaScript 原生接口,与 Ajax 一样允许开发者异步发起 HTTP 请求,但却以更加简单明了的调用方式、基于 Promise 的数据处理方式被称作是 Ajax 的替代品。

fetch("/statics/demosource/demo_json_data.php", {
    method: "POST",
    header: new Headers({"Content-Type" : "application/json"}),
    body: JSON.stringify(data)
})
.then(function (res) {
    return res.ok ? res.json() : Promise.reject(res);
})
.then(function (data) {
    oIpt_country.innerText = data.country;
    oIpt_city.innerText = data.city;
})
.catch(function (res) {
    alert("请求失败:" + res.status);
})

JSON

JSON = JavaScript Object Notation,意思是:JavaScript 对象表示法,是一种轻量级的数据交换格式

语法规则

JSON 的语法可以表示以下三种类型的值:

  • 简单值:使用与 JavaScript 相同的语法,可以在 JSON 中表示numberstringbooleannull,但 JSON 不支持 JavaScript 中的特殊值undefined
  • 对象:对象作为一种复杂数据类型, 表示的是一组无序的键值对,而每个键值对中的值可以是简单值,也可以是复杂数据类型的值;
  • 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,数组的值也可以是任意类型 —— 简单值、对象 或 数组。

简单值

//JSON 表示数值 7
7
//JSON 表示字符串
"JSON is a format for data exchange"

注意

JSON字符串 与 JavaScript字符串 的最大区别在于,JSON 字符串必须使用 双引号,单引号会导致语法错误。

对象

JSON 中的对象与 JavaScript 对象字面量稍微有一些不同。

JavaScript 的对象字面量:

{
    name : "Alan",
    age : 21
}

json表示

{
    "name" : "Alax",
    "age" : 21
}

JSON 对象的键(属性名) 必须双引号

JSON 对象的属性值可以是简单值,也可以是复杂类型值

{
    "name" : "Alan",
    "age" : 21,
    "child" : {
        "name" : "Tim",
        "age" : 7    
    }
}

数组

JSON 数组采用的就是 JavaScript 中的数组字面量形式。

JavaScript 中的数组字面量:

[21, "Alan", false]

json表示

[21, "Alan", false]

总结

  1. 对于 JSON 与 JavaScript 的关系,你现在可以这么理解:JSON 是 JavaScript 对象的字符串表示法,它使用纯文本格式来表示一个 JavaScript 对象的信息,本质上是一个字符串;
  2. 通常,我们会将对象和数组作为 JSON 数据结构的最外层形式,利用它们能够创造出各种各样的数据结构。当然,这不是强制规定的。

JavaScript 内置的 JSON 对象

ECMAScript 5 定义了一个原生的 JSON 对象,可把 JavaScript 对象序列化为 JSON 字符串,或把 JSON 字符串解析为原生的 JavaScript 值。

JSON 对象的方法:

  1. JSON.stringify():用于序列化 JavaScript 对象,将其转换为 JSON 字符串;
  2. JSON.parse():用于解析 JSON 字符串,将其转换为 JavaScript 值。

提示:除了以上两个方法,JSON 对象本身并没有其它作用,也不能被做为构造函数使用。

stringify 方法

JSON.stringify()方法用于将一个 JavaScript 值 / 对象 转换为 JSON 字符串。

var obj = {
    name: "Alan",
    age: 21,
    child: {
        name: "Tim",
        age: 7
    }
};

// 序列化 obj 对象,转换为 JSON 格式的字符串
var json = JSON.stringify(obj)

parse 方法

JSON.parse()方法用于将 JSON 数据解析为原生的 JavaScript 值。

var json = '{"name":"Alan","age":21,"child":{"name":"Tim","age":7}}';

// json 数据本质上是字符串,无法直接访问某一属性
console.log(json.name);    // undefined

// 解析 json,转换为原生的 JavaScript 对象
var obj = JSON.parse(json);
console.log(obj.name);    // 此时可以使用 JavaScript 方法访问某一属性