可以如何发送请求

  • <form>可以发请求,但是会刷新页面或新开页面

    <form action='xxx' method='get'>
        <input type='password' name='password'>
        <input type='submit'>
    </form>
  • <a>只可以发 get 请求,但是也会刷新页面或新开页面
  • <img>只可以发 get 请求,但是只能以图片的形式展示
  • <link>只可以发 get 请求,但是只能以 CSS、favicon 的形式展示
  • <script>只可以发 get 请求,但是只能以脚本的形式运行

❓有没有什么方式可以实现:get、post、put、delete 请求都行,想以什么形式展示就以什么形式展示❓

ajax

Asynchronous Javascript And XML
异步 Javascript 和 XML

需要满足下面三个条件,可以称之为 ajax:

  1. 使用 XMLHttpRequest 发请求
  2. 服务器返回 XML 格式的字符串
  3. JS 解析 XML,并更新局部页面

不过随着技术发展,XML 越来越不常用,经常使用 JSON 代替 XML

version1.0

我们尝试做一个按钮,点击向服务器发送一个请求

html :

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax_html</title>
</head>
<body>
    <button id='myButton'>点我</button>
</body>
<script src='./main.js'></script>
</html>

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建请求
    request.onreadystatuschange=function(){    //当请求的状态有变化时,打印出状态码
        console.log(request.readyStatus);
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路径
    request.send();                            //发送请求
})

后端代码 :

}else if(path==='/xxx'){
    response.statusCode = 200
    response.setHeader('Content-Type', 'text/xml;charset=utf-8')
    response.write(`
        //xml
        <note>
          <to>Tove</to>
          <from>Jani</from>
          <heading>Reminder</heading>
          <body>Don't forget me this weekend!</body>
        </note>
    `)
    response.end()
}

效果 :
当点击 点我 按钮时,首先,新建一个XMLHttpRequest请求;其次,使用 GET 方法请求到 /xxx;最后,发送请求。

当服务器收到请求了 /xxx 路径,然后,就返回了一串格式符合 XML字符串

并且,当请求和响应进行到各个状态时,都会打印出它的状态码
(状态码请参考:https://developer.mozilla.org...
返回xml

version2.0-获取xml

除了可以获取状态码、在浏览器控制台获取服务器返回的XML字符串外,还可以将XML字符串转换为XML
并且可以使用DOM获取元素的API,来获取XML的元素,例如getElementByTagNames...
修改后的 main.js 如下所示,新增内容和注释如??所指。

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建请求
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         ?//表示请求响应都已经完成
            if(request.status === 200){        ?//表示服务器返回成功
                ?//下面三句 let 用来获取返回的 xml,而不再是之前字符串格式
                let parser = new DOMParser();
                ?//返回的内容可以用 responseText 获取
                let xmlDoc = parser.parseFromString(request.responseText,'text/xml');
                let title = xmlDoc.getElementsByTagNames('heading')[0].textContent;
            }
        }
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路径
    request.send();                            //发送请求
})

version3.0-JSON

由于 xml 看上去不够简洁,而且实际使用中,获取的方式比较繁琐(参考上面的三句 let),所以,发明了一个新的数据格式化语言——JSON,用以替代 xml。
(JSON参考官网:http://www.json.org/

JSON vs. Javascript:

  1. 没有 functionundefined
  2. 字符串首尾必须是双引号 ""
  3. 没有 变量
  4. 没有 原型链

修改后端代码,使其返回一个JSON,??为修改处。

后端代码 :

}else if(path==='/xxx'){
    response.statusCode = 200
    response.setHeader('Content-Type', 'text/json;charset=utf-8')    //?xml修改成json
    response.write(`
        //json            //??返回json而不是xml
        {
            "note":{
                "to":"Tove",
                "from":"Jani",
                "heading":"Reminder",
                "body":"Don`t forget me this weekend!"
            }
        }
    `)
    response.end()
}

由于服务器返回的只能是一个字符串(响应第4部分只能是字符串),所以,我们也需要将返回的JSON字符串转换为一个对象,方便操作。

接下来,可以使用js对对象的操作,获取json的内容。

修改后的前端代码,?为修改处。

main.js :

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();         //新建请求
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         //表示请求响应都已经完成
            if(request.status === 200){        //表示服务器返回成功
                console.log("请求成功");
                let string=request.responseText;            ?//获取服务器返回的字符串
                let jsonObject=window.JSON.parse(string);   ?//将返回的字符串变成对象
                console.log(jsonObject.note);               ?//输出对象里的note
                console.log(jsonObject.note.from);          ?//输出对象里的note里的from
            }
        }
    }
    request.open('GET','/xxx');                //初始化,GET 方法,/xxx 路径
    request.send();                            //发送请求
})

同源策略&CORS跨域

<form>表单提交内容不一样,AJAX不刷新页面,并且可以读取响应内容,所以被浏览器认为是不安全的,
所以:

只有 协议+域名+端口 一模一样的时候,才允许发送AJAX请求

Σ(っ °Д °;)っ
那要是我必须需要另一个网站的接口怎么办???

如果http://www.kitty.com想要访问http://www.ben.com的一个接口,那Ben只需要在后端代码里加上一句话,告诉浏览器,http://www.kitty.comhttp://www.ben.com是友好关系,可以互相访问。

Ben的后端代码:

}else if(path==='/xxx'){           //请求的接口
    response.statusCode = 200      //返回状态码
    response.setHeader('Content-Type', 'text/json;charset=utf-8')    //设置返回类型为json
    ?//此句开启友好模式
    response.setHeader('Access-Control-Allow-Origin', 'http://www.kitty.com')
    response.write(`
        //json
        {
            "note":{
                "to":"Tove",
                "from":"Jani",
                "heading":"Reminder",
                "body":"Don`t forget me this weekend!"
            }
        }
    `)
    response.end()
}

添加response.setHeader('Access-Control-Allow-Origin', 'http://www.kitty.com'),浏览器就知道,Kitty和Ben是好朋友,可以互相访问啦!!!

上述方法称之为:
Cross-Origin Resource SharingCORS

使用Javascript发送任意格式的请求

AJAX还允许用户使用Javascript设置一个请求的任意部分,并且可以获取一个响应的任意部分。

一个请求:

GET /xxx HTTP/1.1    //第一部分,方法、地址、协议、协议版本号
HOST:                //第二部分,包含很多行,包含很多内容
Content-Type:        //第二部分,包含很多行,包含很多内容
                     //第三部分,一个回车
这里是请求体          //第四部分

一个响应:

Http/1.1 200 OK      //第一部分,协议、协议版本号、状态码、状态信息
Content-Type:        //第二部分,
                     //第三部分,一个回车
这里是响应体          //第四部分

发送一个任意的请求&获取一个响应任意部分:

myButton.addEventListener=('click',function(e){
    var request= new XMLHttpRequest();            //新建请求
    request.open('GET','/xxx');                   //设置请求第一部分:方法、路径
    request.setRequestHeader('Content-Type','x-www-form-urlencoded);    //设置请求第二部分
    request.send('虽然我是GET,但我也有请求体');    //设置请求第四部分
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         //表示请求响应都已经完成
            if(request.status === 200){        //表示服务器返回成功
                console.log("请求成功");
                console.log(request.status)        //获取响应第一部分,状态码
                console.log(request.statusText)    //获取响应第一部分,状态信息
                console.log(request.getResponseHeader('Content-Type'))    //获取响应第二部分
                console.log(request.getAllResponseHeaders);      //获取所有响应第二部分
                console.log(request.responseText);               //获取响应第四部分
            }
        }
    }
})

总结:
总之,就是AJAX允许用户使用Javascript设置任意一个请求(浏览器不让设置的内容不能设置),也能获取一个响应的任意部分。

使用 JS 设置任意请求 header

  • 第一部分:request.open('get','/xxx')
  • 第二部分:request.setHeader('Content-type','x-www-form-urlencoded')
  • 第四部分:request.send('a=1&b=2')

使用 JS 获取任意响应 header

  • 第一部分:request.status / request.statusText
  • 第二部分:request.getResponseHeader() / request.getAllResponseHeaders()
  • 第四部分:request.responseText

客户端与服务器

封装ajax

我们的目标是只使用一个函数,就能实现下面发送请求的功能:

  • request.open('GET','/xxx');
  • request.onreadystatuschange=function(){}
  • request.send('虽然我是GET,但我也有请求体');

version1.0

封装后的前端代码:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(url,method,body,successFn,failFn){
    var request = new XMLHttpRequest();            //新建请求
    request.open(method,url);                     ?//传入发送请求方式、请求地址
    request.send(body);                           ?//传入请求体
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         //表示请求响应都已经完成
            if(request.status === 200){        //表示服务器返回成功
                successFn.call(undefined,request.responseText);  ?//响应成功,调用传入的成功函数
            }else if(request.status >= 400){
                failFn.call(undefined,request);   ?//相应失败,调用传入的失败函数
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        '/xxx',
        'GET',
        '虽然我是GET,但我也有请求体'
        function(){},
        function(){}
    )
})

实现的功能是一样的,只是将ajax封装在一个函数里,之后调用只需要调用一个函数,出入响应的参数就能实现发送请求的功能。

version2.0

上个版本封装的很好,但是有几个问题:

  • 需要传入的参数太多,可能是不是会忘了需要传入什么参数,无法快速的知道参数含义
  • 当有一个参数用户不想传入的时候,只能使用null进行占位,很麻烦

所以,我们想到,给它传入一个集成的参数,把需要的参数都放在一个对象里,这样可以给参数指定名字,调用时,可以调用对象里的名字。

修改封装后的前端代码:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(option){    ?//由原来的传入一个个的参数,变为传入包含所有参数的对象
    ?//首先获取参数对象里的各个值
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;

    var request = new XMLHttpRequest();            //新建请求
    request.open(method,url);                      //传入发送请求方式、请求地址
    request.send(body);                            //传入请求体
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         //表示请求响应都已经完成
            if(request.status === 200){        //表示服务器返回成功
                successFn.call(undefined,request.responseText);   //响应成功,调用传入的成功函数
            }else if(request.status >= 400){
                failFn.call(undefined,request);    //相应失败,调用传入的失败函数
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        ?//由原来的传入一个个的参数,变成现在传入一个包含所有参数的对象
        {
            url: '/xxx',
            method: 'GET',
            body: '虽然我是GET,但我也有请求体'
            successFn: function(){},
            failFn: function(){}
        }
    )
})

version3.0

我们还需要封装发送请求头:

再次修改封装后的前端代码:

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function(option){    //由原来的传入一个个的参数,变为传入包含所有参数的对象
    //首先获取参数对象里的各个值
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;
    let headers=option.headers;    ?//获取参数对象里的headers
    
    ?//遍历headers,将里面的键、值都设置在请求头里
    for(let key in headers){
        let value=headers[key];
        request.setRequestHeader(key,value);
    }

    var request = new XMLHttpRequest();            //新建请求
    request.open(method,url);                      //传入发送请求方式、请求地址
    request.send(body);                            //传入请求体
    request.onreadystatuschange=function(){    
        console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
        if(request.readyStatus === 4){         //表示请求响应都已经完成
            if(request.status === 200){        //表示服务器返回成功
                successFn.call(undefined,request.responseText);   //响应成功,调用传入的成功函数
            }else if(request.status >= 400){
                failFn.call(undefined,request);    //相应失败,调用传入的失败函数
            }
        }
    }
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        ?//在参数对象里传入headers,里面包括了想要设置的请求头的键、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '虽然我是GET,但我也有请求体'
            successFn: function(){},
            failFn: function(){}
        }
    )
})

运用jQuery使用ajax

请参考jQuery文档:https://api.jquery.com/jQuery...
jquery_ajax

题外话

计算代码执行时间

下列代码可以计算从console.time()console.timeEnd()之间代码执行的时间

console.time()
    //your code
console.timeEnd()

结构化编程:

  • 顺序执行
  • if...else...
  • while/for

ES6解构赋值

使用ES6解构赋值新语法可以简化下列代码:

window.jquery.ajax=function(option){
    let url=option.url;
    let method=option.method;
    let body=option.body;
    let successFn=option.successFn;
    let failFn=option.failFn;
    let headers=option.headers; 
    
    //your code
}

解构之后:

window.jquery.ajax=function(option){
    let {url,method,body,successFn,failFn,headers}=option;
    
    //your code
}

更加高级点:

window.jquery.ajax=function({url,method,body,successFn,failFn,headers}){
    
    //your code
}

?直接从window.jquery.ajax=function(){}的第一个参数里解构,并用let声明!!!

version4.0-Promise

当用户要使用很多ajax的时候,用i定义的或者别人自定义的,这样,每个人ajax的接口名称都不一样,用起来很麻烦,这时候,我们可以使用Promise技术

使用Promise和then:
??为修改处

window.jquery=function(){}

window.$=window.jquery;

window.jquery.ajax=function({url,method,body,headers}){    
                                ?//使用ES6解构新语法,并且删除了successFn、failFn
                                
    ?//promise和之后使用ajax时的then一起,可以自动判断成功函数和失败函数
    return new Promise(function(resolve,reject){ 
    
        //遍历headers,将里面的键、值都设置在请求头里
        for(let key in headers){
            let value=headers[key];
            request.setRequestHeader(key,value);
        }
    
        var request = new XMLHttpRequest();            //新建请求
        request.open(method,url);                      //传入发送请求方式、请求地址
        request.send(body);                            //传入请求体
        request.onreadystatuschange=function(){    
            console.log(request.readyStatus);      //当请求的状态有变化时,打印出状态码
            if(request.readyStatus === 4){         //表示请求响应都已经完成
                if(request.status === 200){        //表示服务器返回成功
                    ?//响应成功,调用传入的成功函数,修改sucessFn为resolve
                    resolve.call(undefined,request.responseText); 
                }else if(request.status >= 400){
                    ?//响应成功,调用传入的成功函数,修改failFn为reject
                    reject.call(undefined,request);    //相应失败,调用传入的失败函数
                }
            }
        }
    })
}

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        //在参数对象里传入headers,里面包括了想要设置的请求头的键、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '虽然我是GET,但我也有请求体'
        }
    ).then(function(){},function(){})    
    ?//这就是promise,就是在ajax后面加上then,
    ?//传入第一个函数会被当成是成功时调用的函数,第二个函数会被当成失败时调用的函数
})

如果需要多次处理:

//your code

myButton.addEventListener=('click',function(e){
    window.jquery.ajax(
        //在参数对象里传入headers,里面包括了想要设置的请求头的键、值
        {
            url: '/xxx',
            method: 'GET',
            headers: {
                'content-type': 'application/x-www-form-urlencoded',
                'color': 'red'
            }
            body: '虽然我是GET,但我也有请求体'
        }
    ).then(
        function(){
            //your code
            return xxx;
        },
        function(){})
     .then(
         function(){},
         function(){})    
    ?//这就是promise,就是在ajax后面加上then,
    ?//传入第一个函数会被当成是成功时调用的函数,第二个函数会被当成失败时调用的函数
    ?//当需要多次调用,可以在then的函数里直接return,return的内容会被当成之后then的输入,直接处理
})

BreezingSummer
45 声望0 粉丝