app.post('/postcors',(request,response)=>{ // 设置响应头. 名字,* (设置所有协议、域名、端口、允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 指定端口允许跨域 response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8082'); // 响应头,允许所有的响应头类型 response.setHeader('Access-Control-Allow-Headers','*'); // 设置响应体 const data = { name:'cors JSON', type:'POST JSON Type' } response.send(JSON.stringify(data)); });
Ajax基础
1.1ajax简介
AJAX全称是Asynchronous JavaScript And XML,就是异步的JS和XML
通过Ajax可以在浏览器中向服务器发送异步请求,最大的优势是:无刷新获取数据。
Ajax不是新的编程语言,而是一种将现有的标准组合在一起使用的新方式。
1.2XML简介
xml 可扩展标记语言
xml 被设计用来传输和保存数据
xml和HTML类似,,不同的是HTML中都是预定义标签;而XML中没有预定义标签,全都是自定义标签,用来保存一些数据
最开始Ajax在进行数据交换的时候,使用的格式就是XML,服务器端给浏览器返回结果时就是返回XML格式的字符串,前端的JS在接收到结果之后对XML格式的字符串进行解析,把数据提取出来。
但是现在使用AJAX时候就不再使用XML了,而是换成了JSON格式,JSON相对于XML更加简洁,在数据转换方面也相对容易、更灵活。
<student> <name>名字</name> <age>年龄</age> <gender></gender> </student>
1.3AJAX的特点
1.3.1AJAX的优点
可以无需刷新页面与服务器进行通信允许你根据用户事件来更新部分页面内容
1.3.2AJAX的缺点
没有浏览历史,不能回退存在跨域问题(同源)SEO(搜索引擎优化)不友好,网页爬虫爬不到,源代码(响应体)第一次请求时没有要请求内容的信息,他是通过Ajax向服务端发请求,服务端返回结果,然后js动态创建到页面中的,爬虫爬不到数据。IE缓存问题:ie浏览器会对Ajax请求结果进行缓存,把结果缓存起来;当下一次再去请求数据时候,会到缓存里找,而不是服务器返回的最新的请求结果,这样的话可能会影响结果,解决的办法是在url?后面加一个时间戳,让浏览器知道这是两次不同的请求。
1.4HTTP协议
hypertext transport protocol 超文本传输协议,协议详细规定了浏览器和万维网服务器之间互相通信的规则。
协议就是一种约定、规则。主要约定了请求报文、响应报文这两块的内容。
请求报文
格式:
一个完整的HTTP请求报文 请求行:{ 请求类型GET/POST URL路径 HTTP协议版本1.0 / 1.1 / 2.0 } 请求头:{ Host: baidu.com Ciikie: name=baidu Content-type: application/x-www-from-urlencoded //告知服务器我的请求体是什么类型的 User-Agent: chrome 83 } 空行:{} 请求体:{//如果是get请求,请求体是空的;如果是POST请求,请求体可以不为空 username=admin&password=admin } 一个完整的HTTP响应报文 行:HTTP/1.1 200 OK (协议版本 响应状态码 响应状态字符串) 头: Content-type:text/html;charset=utf-8 //类型 就是对响应体内 容进行一些相关的描述 Content-length:2048 //长度 Content-encoding:gzip //压缩方式 空行:必须要有 体: <html> <body> <h1> html响应的内容是放在了响应的报文当中,浏览器在接到结果之后会把响应体结果提取出来,对内容进行解析,在页面渲染并显示 </h1> </body> </html>
参数:
1.4.1 Express
// 1. 引入express const express = require('express'); // 2. 创建应用对象 const app = express(); // 3. 创建路由规则 request是对请求报文的封装 response是对响应报文的封装 app.get('/server',(request,response)=>{ // 设置响应头. 名字,* (设置允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 设置响应体 response.send('Hello 原生AJAX!'); }); // 4. 监听端口启动服务 app.listen(8082,()=>{ console.log("服务已经启动8082端口监听中......"); })
1.5AJAX的应用
1.5.1原生Ajax GET请求
只需要四步:
- 创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
- 设置请求的方法和url
xhr.open('GET','//127.0.0.1:8082/server');
- 发送
xhr.send();
- 处理服务端返回的结果
xhr.onreadystatechange = function(){}
当xhr对象里的readystate属性改变时处理。
readyState四个值的意义
- 0 未初始化
- 1 open方法已经调用完毕
- 2 send方法已经调用完毕
- 3 服务端返回了部分结果
- 4 服务端返回了所有结果
<button id="btn">点击发送请求</button> <div id="result"></div> <script> const btn = document.getElementsByTagName("button")[0]; const result=document.getElementById("result"); btn.onclick=function(){ // ajax的操作 // 1. 创建对象, const xhr = new XMLHttpRequest(); // 2. 设置请求的方法和url xhr.open('GET','http://127.0.0.1:8082/server'); // 3. 发送 xhr.send(); // 4. 事件绑定,处理服务端返回的结果 // on==when. readystate是xhr对象中的属性,表示状态 .change 改变会改变四次 // 0未初始化 1(open方法已经调用完毕) 2(send方法已经调用完毕) 3(服务端返回了部分结果) 4(服务端返回了所有结果) xhr.onreadystatechange = function(){ // 判断服务端是否返回了所有结果 if(xhr.readyState === 4){ //判断响应状态码 if(xhr.status >= 200 && xhr.status < 300){ // 处理结果 行 头 空行 体 // 1.响应行 console.log(xhr.status);//状态码 console.log(xhr.statusText);//状态字符串 console.log(xhr.getAllResponseHeaders.toString());//所有响应头 console.log(xhr.response);//响应体 result.innerHTML = xhr.response; }else{ } } } } </script>
设置参数
可以在url后面直接加上?参数名:值&参数名:值…
xhr.open('GET','//127.0.0.1:8082/server?name=admin&password=admin');
POST请求
<script> const RESULT = document.getElementById("RESULT"); RESULT.addEventListener("mouseover",function(){ // 1. 创建对象 const xhr = new XMLHttpRequest(); // 2.初始化,设置请求类型 与 方法 xhr.open('POST','http://127.0.0.1:8082/server'); // 3. 发送请求 xhr.send(); // 4.事件处理 xhr.onreadystatechange=function(){ if(xhr.readyState === 4){ if(xhr.status >=200 && xhr.status<300){ RESULT.innerHTML=xhr.response; // console.log(xhr.getAllResponseHeaders()); } } } }); </script>
报错信息 Access to XMLHttpRequest at 'http://127.0.0.1:8082/server' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. 已拦截跨源请求:同源策略禁止读取位于 http://127.0.0.1:8082/server 的远程资源。(原因:CORS 头缺少 'Access-Control-Allow-Origin')。状态码:404。 因为服务端没有一个与之匹配的路由规则,而且没有设置响应头
参数设置
// 在send方法里可以设置参数,可以设置任意类型,任意格式的数据 xhr.send('a=100&b=200&c=300');
1.6设置请求头信息
// 设置请求头信息 一般设置一些预定义的请求头 // Content-Type 用来设置请求体的内容类型 // application/x-www-form-urlencoded 设置参数查询字符串的类型 // 请求头信息也可以自定义,但是浏览器会有安全机制, xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); // 设置请求头的信息是因为 要把身份校验的信息放在头信息里面,把他传递给服务器,由服务器对参数做提取,对用户的身份进行校验 xhr.setRequestHeader('name','yxj');
后端操作
// all表示可以接受所有类型的请求 app.all('/server',(request,response)=>{ // 设置响应头. 名字,* (设置允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 响应头,允许所有的响应头类型 response.setHeader('Access-Control-Allow-Headers','*') // 设置响应体 response.send('Hello 原生AJAX POST!'); });
2.1服务端响应JSON数据
nodemon工具可以实时重启服务器
<script> const result = document.getElementById("result"); // 绑定键盘按下事件 window.onkeydown=function(){ // 1. 获取xhr对象 const xhr = new XMLHttpRequest(); // 设置响应体数据类型 xhr.responseType = 'json'; // 2.初始化 xhr.open('GET','http://127.0.0.1:8082/json-server'); // 3.发送 xhr.send(); // 4. 处理数据 xhr.onreadystatechange=function(){ if(xhr.readyState === 4){ if(xhr.status >=200 && xhr.status <300 ){ // 手动将数据转换 // let data = JSON.parse(xhr.response); // result.innerHTML= data.name; // console.log(data); // 自动转换 console.log(xhr.response); result.innerHTML= xhr.response.name; } } } } </script>
后台
app.all('/json-server',(request,response)=>{ // 设置响应头. 名字,* (设置允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 响应头,允许所有的响应头类型 response.setHeader('Access-Control-Allow-Headers','*') // 设置一个响应数据 const data={ name:"winter", age:18, sex:"man" } // 设置响应体,send方法里只能接收字符串和Buffer 类型 //所以要对对象进行一个转换 let str = JSON.stringify(data); response.send(str); });
2.2解决ie缓存问题
xhr.open('GET','http://127.0.0.1:8082/json-server?t='+Date.now()); //加一个时间戳,让浏览器知道这是两次不同的请求。
2.3超时与网络错误的处理
//超时设置 请求超过2s则取消请求 xhr.timeout = 2000; // 超时回调 实际中应该采取更加友好的方式 xhr.ontimeout = function(){ alert("网络请求超时!"); } // 网络异常回调 xhr.onerror = function(){ alert("网络异常!"); } xhr.open('GET','http://127.0.0.1:8082/out?'+Date.now()); xhr.send();
2.4取消AJAX请求
const xhr = new XMLHttpRequest(); xhr.abort();
2.5Ajax重复发送请求情况
如果客户端一直向服务器发送相同的请求,服务器的压力就会很大,会接收到很多的相同请求。
解决办法:
在向服务器发送请求之前先判断之前有没有发起过相同的请求,如果有就将之前的请求取消掉,重新发起请求。这样一来我们向服务器发起的请求始终只有一个,服务器压力就会小一些
<script> const btn = document.getElementsByTagName("button")[0]; const result=document.getElementById("result"); let xhr = null; //标识变量 let isSending = false; //是否正在发送AJAX请求 btn.onclick=function(){ // 判断标识变量 if(isSending) xhr.abort();//如果正在发送,则取消该请求,创建一个新的请求 xhr = new XMLHttpRequest(); // 修改 标识变量的值 isSending = true; xhr.open('GET','http://127.0.0.1:8082/out?'+Date.now()); xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ //修改标识变量 isSending = false; if(xhr.status >= 200 && xhr.status < 300){ result.innerHTML = xhr.response; console.log(xhr.response); }else{ } } } } </script>
3.1JQuery发送AJAX请求
<script> $('button').eq(0).click(function(){ //$.get/post方法接收的是参数 // 参数1:url 参数2:要发送的对象{} 参数三:回调函数(响应体)操作服务器端返回的数据 参数4:设置响应体类型 $.get('http://127.0.0.1:8082/getjquery',{a:100,b:200},function(data){ console.log(data);//对象 },'json') }); $('button').eq(1).click(function(){ $.post('http://127.0.0.1:8082/postjquery',{a:300,b:400},function(data){ console.log(data);//没+json会返回字符串 }) }); $('button').eq(2).click(function(){ //$.ajax方法接接收的是一个对象,对象里有响应的属性,通过属性设置参数 $.ajax({ // url: url:'http://127.0.0.1:8082/alljquery', // 参数,post请求才能设置请求体 data:{a:100,b:200}, // 类型 type:'POST', // 响应体结果 dataType:'json', // 成功的回调 success:function(data){ console.log(data); }, // 超时时间 timeout:8000, // 失败的回调 超时、网络异常 error:function(){ console.log("出错了"); }, // 头信息设置 headers:{ name:'headers', msg:'hiashfi', } }); }); </script>
app.get('/getjquery',(request,response)=>{ // 设置响应头. 名字,* (设置允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 设置响应体 const data = { name:'Jquery JSON', type:'get JSON Type' } response.send(JSON.stringify(data)); }); // JQuery post app.post('/postjquery',(request,response)=>{ // 设置响应头. 名字,* (设置允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 响应头,允许所有的响应头类型 response.setHeader('Access-Control-Allow-Headers','*'); // 设置响应体 const data = { name:'Jquery JSON', type:'POST JSON Type' } response.send(JSON.stringify(data)); });
4.1 axios
Axios 是一个基于 promise 网络请求库,作用于node.js 和浏览器中。 在服务端它使用原生 node.js http
模块, 而在客户端 (浏览端) 则使用 XMLHttpRequests。
特性:
- 从浏览器创建 XMLHttpRequests
- 从 node.js 创建 http请求
- 支持 Promise API
- 拦截请求和响应
- 转换请求和响应数据
- 取消请求
- 自动转换JSON数据
- 客户端支持防御XSRF
4.12发起一个get请求
// 向给定ID的用户发起请求 axios.get('/user?ID=12345') .then(function (response) { // 处理成功情况 console.log(response); }) .catch(function (error) { // 处理错误情况 console.log(error); }) .then(function () { // 总是会执行 });
4.13 发起一个 POST 请求
axios.post('/user', { firstName: 'Fred', lastName: 'Flintstone' }) .then(function (response) { console.log(response); }) .catch(function (error) { console.log(error); });
发起多个并发请求:
function getUserAccount() { return axios.get('/user/12345'); } function getUserPermissions() { return axios.get('/user/12345/permissions'); } Promise.all([getUserAccount(), getUserPermissions()]) .then(function (results) { const acct = results[0]; const perm = results[1]; });
4.1.4 可以向 axios 传递相关配置来创建请求
// 发起一个post请求 axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred', lastName: 'Flintstone' } });
// 在 node.js 用GET请求获取远程图片 axios({ method: 'get', url: 'http://bit.ly/2mTM3nY', responseType: 'stream' }) .then(function (response) { response.data.pipe(fs.createWriteStream('ada_lovelace.jpg')) });
5.1使用fetch函数发送Ajax请求
fetch函数属于全局对象,可以直接调用,返回的结果是promise对象
btns[0].onclick = function(){ // fetch(url,{}) fetch('http://127.0.0.1:8082/allaxios',{ // 请求方法 method:'POST', // 请求头 headers:{ name:'fetch' }, // 请求体 body:'username=admin,password=123456' }).then(response=>{ // console.log(response); // return response.text();//转成string return response.json();//转成json }).then(response=>{ console.log(response); }) }
6.1同源策略
same-Oringin Policy最早由Netscape公司提出,是浏览器的一种安全策略
**同源:**协议、域名、端口号必须完全相同。
- 就是来自相同的一个服务
Ajax是默认遵循同源策略的。不满足同源策略是无法直接发送Ajax请求的。
例:
- 当前网页http://a.com 8000端口
- 目标资源的协议也必须是http协议,a.com域名,8000端口
跨域原因:
- 因为单台服务器,能力受限
- 加入更多的服务器提供更多的服务
6.2跨域的解决
6.2.1 JSONP
jsonp是什么?
- JSONP(JSON with Padding),是一个非官方的解决方案,纯粹凭借程序员的聪明才智开发出来的,只支持get请求。
jsonp怎么工作?
- 在网页有一些标签天生具有跨域能力,比如:img、link、iframe、script.
- jsonp利用script标签的跨域能力来发送请求的。
jsonp的使用
- 动态创建一个script标签
var script = document.createElement("script");
- 设置script的src,设置回调函数
script.src = "http://localhost:3000/testAJAX?callback=abc";
7. CORS
// 设置响应头. 名字,* (设置所有协议、域名、端口、允许跨域) response.setHeader('Access-Control-Allow-Origin','*'); // 指定端口允许跨域 response.setHeader('Access-Control-Allow-Origin','http://127.0.0.1:8082'); // 响应头,允许所有的响应头类型 response.setHeader('Access-Control-Allow-Headers','*'); // 允许所有请求类型(get、post、...) response.setHeader('Access-Control-Allow-Method','*');
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS
CORS是什么?
CORS (Cross - Origin - Resource Sharing), 跨域资源共享。
CORS是官方的跨域解决方案,他的特点是爱不需要在客户端做任何特殊的操作,完全在服务器中进行处理,支持get和post请求。
跨域资源共享新增了一组HTTP首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。
CORS是怎么工作的?
CORS通过设置一个响应头来告诉浏览器,该请求允许跨域,浏览器收到该响应以后就会对响应放行。
CORS的使用
主要是服务器里的设置:
router.get(“/testAJAX”,function(req,res){
})
btn.onclick = function(){ // 创建对象 var xhr = new XMLHttpRequest(); // xhr.open('GET','http://127.0.0.1:8082/getcors'); // xhr.send(); xhr.onreadystatechange = function(){ if(xhr.readyState === 4){ if(xhr.status >= 200 && xhr.status<300){ console.log(xhr.response); } } } }