可以如何发送请求
-
用
<form>
可以发请求,但是会刷新页面或新开页面 - 用
<a>
只可以发 get 请求,但是也会刷新页面或新开页面 - 用
<img>
只可以发 get 请求,但是只能以图片的形式展示 - 用
<link>
只可以发 get 请求,但是只能以 CSS、favicon 的形式展示 - 用
<script>
只可以发 get 请求,但是只能以脚本的形式运行
❓有没有什么方式可以实现:get、post、put、delete 请求都行,想以什么形式展示就以什么形式展示❓
ajax
Asynchronous Javascript And XML
异步 Javascript 和 XML需要满足下面三个条件,可以称之为 ajax:
- 使用 XMLHttpRequest 发请求
- 服务器返回 XML 格式的字符串
- JS 解析 XML,并更新局部页面
不过随着技术发展,XML 越来越不常用,经常使用 JSON 代替 XML
version1.0
我们尝试做一个按钮,点击向服务器发送一个请求
html :
ajax_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`) response.end()} Tove Jani Reminder Don't forget me this weekend!
效果 :
当点击 点我 按钮时,首先,新建一个XMLHttpRequest
请求;其次,使用 GET 方法请求到 /xxx;最后,发送请求。 当服务器收到请求了 /xxx 路径,然后,就返回了一串格式符合 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参考官网:)JSON vs. Javascript:
- 没有 function,undefined
- 字符串首尾必须是双引号 ""
- 没有 变量
- 没有 原型链
修改后端代码,使其返回一个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.com
和http://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 Sharing → CORS使用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文档:
题外话
计算代码执行时间
下列代码可以计算从console.time()
到console.timeEnd()
之间代码执行的时间
console.time() //your codeconsole.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 codemyButton.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的输入,直接处理})