js网络请求和跨域网络请求自总结
目前掌握的:
- 标签(JSONP)
- AJAX
- AJAX的JQ写法
- AXIOS
- 微信小程序下的axios封装
- 补充:promise封装ajax网络请求
- Node
- CORS
基础
前端的网络请求:
1.客户端 向 服务器发起请求(request)
2.服务器 接受到请求之后会对本次请求做分析,了解请求的意图(获取资源? 文件? 数据... ),
分析出之后服务器进行对应的操作,最后对本次请求做出响应(response), 返回给前端所需要的东西.
我们前端的 哪些 操作或者代码会发起网络请求
1. 有些标签, 比如具有src或者href属性,或者url样式的标签, 都可以发起网络请求 只能是get
2. form表单 get/post
3. 浏览器的地址栏写网址, 回车 get
4. ajax发起网络请求 get/post
GET请求和POST请求
----------是将来最常用的网络请求方式
注意:
不能字面意义上理解get和post请求, 这两种请求都可以既向服务器获取数据, 也可以向服务器传递数据
GET和POST请求的区别:
1 GET提交的数据会拼接在网址的后面, 以? 开头,键值对之间用& 链接, 是可见的;
POST请求会把请求的数据存放在请求体里, 不拼接在url网址的后面, 不可见.
2.GET请求提交数据的大小由要求, 最大只有1024字节;
而POST没有大小限制
4.GET方式提交数据, 会带来安全性问题, 因为是可见的;
POST不可见相对来说较安全
一.标签:
使用标签,标签没有同源策略,所以它既可以进行网络请求,也可以完成跨域,如以下标签:
<img />、<a />、<form></form>、<script />
以前端为主导的跨域方法—JSONP
JSONP实现跨域的原理:标签没有同源策略,所以利用script标签完成跨域
1. 动态创建script标签
2. 给script标签设置src为要请求的url, 如果有参数, 直接拼接到网址的后面
3. 注意, 需要在所有的参数的最后在拼接一个参数, 这个参数需要前后台进行配合来规定
4. 将script标签插入到当前文档中(此时才发起网络请求)
5. 将script标签删除
// An highlighted block
var scriptE = document.createElement("script");
scriptE.src = "网址";
//scriptE.src = "url?name=张三&num=2020&cb=succcess";
//创建标签
document.body.appendChild(scriptE);
//销毁标签
document.body.removeChild(scriptE)
function succcess(data) {
console.log(data);
}
二.AJAX --不可跨域
1.AJAX原生写法
AJAX原生JS写法,无法完成跨域请求—JS有同源策略
// AJAX: Asychronous JavaScript And XML
异步 javascript 和 xml
不是一门新的语言, 还是JS
作用: 异步发起网络请求, 局部刷新页面
网络请求状态:
在一次网络请求的过程中, 网络请求的状态(readytstate)是不断改变的, 由以下几种状态,
我们通过readystate的值进行判断, 该值是一个number类型
1: 请求建立链接
2: 请求已发送, 正在处理
3: 请求已处理, 并已得到部分数据
4: 请求已完成, 并已得到所有数据 ****
网络请求成功和失败实际上服务器都会给我们返回数据, readystate的值都是4,
所以我们去在readystate == 4的前提下保证本次请求时成功的
通过http的状态码判断本次请求的状态:
1xx: 收到请求, 正在处理
2xx: 成功类型, 并已经处理 ****
3xx: 重定向类型, 请求发生变化
4xx: 客户端错误
5xx: 服务端错误
注意:
不要把 "请求已完成, 并已得到所有数据" 与 "成功获取到后台返回的数据"划等号
get请求
// An highlighted block
function getReq() {
/*
1. 创建网络请求对象,XMLHttpRequest对象, 该对象是ajax的核心
注意: ie浏览器发起网络请求通过ActiveXObject对象
*/
if (window.XMLHttpRequest) {
//不是ie
var request = new XMLHttpRequest();
} else {
//ie
var request = ActiveXObject("Microsoft.XMLHTTP");
}
/*
2. 监听网络请求,处理请求成功时的状态
*/
request.onreadystatechange = function () {
//该事件会在网络请求的状态发生改变时触发
if (this.readyState == 4) {
//请求已完成, 并已得到所有数据
if (this.status >= 200 && this.status < 300) {
//本次请求完毕,且本次请求是成功的, 就可以获取本次请求的数据,
//数据存储在网络请求对象的responseText属性里
console.log(this.responseText);
}
}
}
/*
3. 设置请求的方式和请求地址和参数
open(请求方式, 请求地址, 是否异步请求, 用户名, 密码);
*/
request.open("GET", "网址(可以用字符串拼接的方法传递参数)", true);//理解为开启一个请求
/*
4. 发起请求
*/
request.send();
}
post请求
// An highlighted block
function postReq() {
//1. 创建网络请求对象
if (window.XMLHttpRequest) {
// 不是ie
var request = new XMLHttpRequest();
} else {
// ie
var request = ActiveXObject("Microsoft.XMLHTTP");
}
request.withCredentials = true;
//2. 设置监听
request.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status >= 200 && this.status < 300) {
console.log(this.responseText);
}
}
}
//3. 设置请求方式和请求地址
request.open("POST", "地址", true);
//4. 设置请求头
/*
* 因为get请求的时候, 参数会直接拼接到网址的后面, 所以系统明确的知参数的格式,
* 而post请求把数据存放在请求体里, 这个时候需要告诉服务器数据的格式
* application/x-www-form-urlencoded表示数据的格式为键值对的格式
*/
request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
//5. 发起网络请求并添加参数
request.send(参数);
//request.send("name=" + i1.value + "&num=" + i2.value);
}
三.AJAX的jQuery写法
JQ里也有ajax请求, 是对原生ajax的封装, 语法上更统一和简便
语法:
$.ajax(配置对象);
url: 发起请求的地址
type: 请求的方式(GET/POST)
data: 需要传递的数据, 是对象格式
dataType: 预期的服务器相应的数据类型
success: 请求成功的回调函数
error: 请求失败的回调函数
jsonp: 跨域时使用, 跨域键值对参数的key
jsonpCallback: 跨域时使用, 跨域键值对参数的value(函数名)
contentType: 数据发送到服务器时的格式
timeout: 设置的超时时间, 单位:毫秒
async: 布尔值, 是否异步, 默认值true
// An highlighted block
$.ajax({
type:"get",
url:"",
async:true
});
四.axios
axios是通过promise实现对ajax技术的一种封装,就像jQuery实现ajax封装一样。
简单来说: ajax技术实现了网页的局部数据刷新,axios实现了对ajax的封装。
axios是ajax,ajax不止axios。
axios({
url: '/getUsers',
method: 'get',
responseType: 'json', // 默认的
data: {
//'a': 1,
//'b': 2,
}
}).then(function (response) {
console.log(response);
console.log(response.data);
}).catch(function (error) {
console.log(error);
})
})
vue下的axios
main.js
//导入axios网络请求模块和vue-axios模块
import axios from "axios";
import VueAxios from "vue-axios";
//对axios进行全局注册,注意axios全局注册必须发生在根Vue实例被创建之前,
//并且创建成功之后,axios会作为所有vue实例的属性进行调用
Vue.use(VueAxios, axios);
Vue.prototype.$axios = axios;//给Vue函数添加一个原型属性$axios 指向Axios
//get
this.axios.get("https://elm.cangdu.org/v2/index_entry");
//get(多条\携带参数)
const request1 = this.axios.get("url");
const request2 = this.axios.get("url?id=" + id+ "&name=" + name);
this.axios.all([request1, request2])
.then(this.axios.spread((res1, res2) => {
this.x1= res1.data;
this.x2 = res2.data;
})).catch(err => console.log(err));
//post
this.axios.post(this.$url + "/v2/login", values);
this.axios.post("https://xxxx.org/x1/x2/"+newAddress.id)
.then((res) => {
console.log(res.data);
}).catch((err) => {
console.log(err);
});
vue.config.js
//开发环境下通过配置代理完成跨域请求操作
devServer: {
proxy: {
'/api': {
target: "xxxxx", // 你请求的第三方接口
changeOrigin: true,
//在本地会创建一个虚拟服务端,
//然后发送请求的数据,并同时接收请求的数据,
//这样服务端和服务端进行数据的交互就不会有跨域问题
pathRewrite: { // 路径重写,
'^/api': 'https://xxxx.com'
// 替换target中的请求地址,
//也就是说以后你在请求https://xxxx.com这个地址的时候直接写成/api即可。
}
},
}
},
五.微信小程序下的axios封装
network.js
const site = require("siteInfo.js")
//封装网络请求
function start_network(params) {
return new Promise(function (resolve , reject) {
wx.request({
...params,
success:function (res) {
resolve (res);
},
fail:function (error) {
reject(error);
}
})
});
}
//进行网络请求 (自己封装一个axios) async代表axios中存在一个异步存在的任务
async function axios(params){
//网址url和额外参数params传过来
// console.log(typeof url);
// console.log(url instanceof Array);
let results = ""; //存储网络请求的返回数据
//如果传过来的是一个数组,则证明是多个网络请求
if(params instanceof Array){
results = [];
for(let i = 0; i < params.length; i++){
//处理相对网址
let param = params[i];
param.url = site.siteroot+param.url;
//await 等待有结果返回
let result = await start_network(params[i]);
results.push(result);
}
}else{
params.url = site.siteroot+params.url;
results = await start_network(params);
}
return results;
}
// module.exports = {axios};
module.exports = axios;
siteInfo.js
/**
* 配置文件
*/
module.exports = {
name: "xxxxx",
siteroot: "http://xxx.xxx.xxx.xxx/", // 必填: api地址,结尾要带/
};
xxxx.js
const axios = require("../../common/network.js")
//get
axios({
url:"xxxxxxxx"
}).then((res)=>{
// console.log(res);
this.setData({
xxxx : xxxxxx
})
})
//post
axios({
url:"xxx",
method:'POST',
data:{
//post传递的值
id:"10001",
name:"",
//......
}
}).then((res)=>{
// console.log(res);
axios({
url:"xxxxx"
}).then((res)=>{
// console.log(res);
this.setData({
//......
})
})
})
六.promise封装ajax网络请求 (并使用node.js完成跨域)
解决的问题—将一个异步执行的网络请求具备同步执行的顺序
network.js
//定义一个函数function 叫网络请求network
//network专门用来完成Ajax请求
//network返回的是一个promise对象,该对象内部封装一个ajax请求的异步任务
function network (params) {
//params变量为对象类型, 用来存储网络请求需要传输的数据参数
//诺言 在构建实例的时候,他的回调函数里面放的就是一个异步执行的任务,
//所以把ajax操作封装到promise中.
return new Promise(function (resolve, reject) {
// 基于ajax请求的请求操作
// 1.创建Ajax对象 (三目运算符 判断浏览器类型)
let ajax = window.XMLHttpRequest ? (new XMLHttpRequest())
: (new ActiveXObject("Microsoft.http"));
// 2.完成Ajax链接 open 规定请求的类型、URL 以及是否异步处理请求。
let method = params.method ? "".toUpperCase : "GET";
//从外界获取请求类型,.toUpperCase是大写转化
ajax.open(method, params.url);
// 3.将请求发送到服务器。
if (method == "POST") {
// 链接成功以后发送数据(该操作针对POST请求方式对应的数据)
ajax.send(params.body);
//body是用来向服务器发送post参数,必须是字符串数据,
//如果外界传输的是对象类型,需要转化成字符串类型
} else {
ajax.send();
}
// 4.使用ajax监听状态码的变换
ajax.onreadyStateChange = function () {
if (ajax.onreadyState == 4) {
if (ajax.states == 200) {
//网络请求成功
resolve(ajax.responseText);
//responseText--响应结果
} else {
//网络请求失败
reject({ 'code': ajax.status, 'msg': "网络请求失败" })
}
} else {
//ajax链接失败
reject({ 'code': ajax.status, 'msg': "ajax链接失败" })
}
}
});
//仅仅进行ajax操作,无法在进行异步请求的同时具备同步执行顺序
}
//将上面的network异步任务设置成同步执行顺序,
// async修饰某一个函数是代表被修饰的函数内部存在一个异步执行的任务.
//如果函数内部使用了await,此时await所在的函数必须使用async进行修饰
// async修饰的函数最终的返回结果仍然是一个Promise实例
async function start_net_work (params) {
//await直接修饰一个返回结果为promise实例的函数,
//作用是等待promise的返回结果.
//计算机在运行到await会将await下面的代码进行缓存,
//此时程序去执行其他任务, 等到await有结果返回值,
//程序再将缓存的代码取出继续进行后面的操作.
let result = await network(params);
//await 对应的外层函数必须用async修饰
console.log(result);
return result;
}
//将网络请求以模块的形式导出给结果
export default { start_net_work };
xxx.html
<script type="text/babel" src="./network.js"></script>
<script type="module">
import net from "./network.js"
net.start_net_work({
// url: "url"
//同源策略,出现跨域问题
//node--主要解决跨域问题(代理跨域) 搭建一个本地node服务器
//在工作区新建一个Node_serve文件夹,
//在里面构建一个属于自己的node服务器工程,选中Node_serve文件夹
//ctrl+~ 打开终端进入 Node_serve中,npm init 回车到结束,
//在node_serve中出现一个依赖文件package.json
//自己的端口5500 我们要切换到8080,
//5500访问8080本身就时跨域,所以要用CROS解决
//新建一个index.js 程序入口
url: "http://localhost:8080",
method: "GET" //post
//xxx
})
</script>
index.js
express = require("express");
//导入cors模块
cors = require('cors');
https = require('https'); //主要用来完成 https协议 的网络请求
//创建一个App应用
app = express();
//设置app应用的cors跨域 解决5500和8080的跨域问题
//首先>npm install cors -s /node_modules资源包
//导入cors模块cors = require('cors');
//.use() 初始化一个cors实例,就使用了cors跨域,
//允许所有域名访问当前服务器
app.use(cors());
//开始进行网络请求
//"/"--根路由localhost:8080 req网络请求对象 res响应对象
app.get('/', function (req, res) {
//完成网络请求 (本地node向激素数据发起网络请求)
//发起网络请求需要https模块 ,所以要导入https模块
//get方法里面放置的就是网址
https.get("url", function (response) {
//该回调函数的response实际上指代当前https网络请求对应的响应对象
var data = ""; //提前定义一个数据源, 这就是从服务端拿到的数据
response.on("data", function (chunk) {
//当有数据接收的时候调用
data += chunk; //将接受到的数据片段,进行拼接
});
//当数据全部接受完毕时,执行res.on
response.on("end", function () {
//将接受到的数据传递给前端页面
res.send(data);
});
});
});
// 监听接口
app.listen(8080);
// run Code 启动node服务器
七.Node
node.js的原生写法 需要使用http\https\url等模块,各种请求方式不同比较复杂,所以使用express模块.
express模块是对http模块的简单封装(类似于JS和JQ的关系), 提供了一系列强大特性帮助我们创建各种Web应用, 统一且快速的对前端的请求做出响应。
//node里不自带express模块,所以需要手动下载
npm i express --save
1.引入express模块
const express = require("express");
2.初始化一个应用
const app = express();
express方法在调用之后会得到一个app(自定义)对象,
这一个服务器对象, 该对象有三个常用的方法,实现了类型监听请求类型和路由的功能
1. app.get() 用来监听get请求
2. app.post() 用来监听post请求
3. app.all() 用来监听所有请求
以上方法在调用时, 都可以传入两个参数
参数1: 路由
a. 字符串 "/get", "/ajaxPost"
b. 正则表达式
参数2: 回调函数, 该函数会在符合当前请求类型和路由的请求发生时触发,
回调里有两个参数, req请求对象和res响应对象
【req】对象的常用属性:
1. query, url的参数对象
2. path, 文件路径(路由)
【res】对象的常用方法
1. res.json(数字/字符串/json串/数组/对象); 向前端返回数据;
如果填入的是原始数据类型, 系统会自动转化, 并返回,
而且前端拿到的直接就是原始数据类型
2. res.send(/字符串/json串/数组/对象); 向前端返回数据;
3. res.sendFile(); 向前端返回静态资源;
4. res.jsonp(); 用于在jsonp请求时向前端返回数据
get请求
//监听get请求以及路由,
注意:express在匹配请求时, 是从上到下匹配的,会进行遍历
//特点是每个请求都有自己的回调,都有自己的请求和响应对象,代码很清晰
app.get("/get", function (req, res) {
concole.log("get请求的/get被触发!");
res.end("get");
});//127.0.0.1:7654/get //输出:get请求的/get被触发!
app.get("/abc", function (req, res) {
concole.log("get请求被触发!");
concole.log(res.query.username);
res.end("get");
});//127.0.0.1:7654/abc?username=张三
// 输出:get请求的/abc被触发! 张三
post请求
在get请求中,有query方式,获取url中的数据传值,但是post请求并不会把数据写在url中,有两种解决方案,一种是之前的事件监听,另一种是利用body-parser模块, 统一获取数据, body-parser配置完毕之后, 通过req.body获取post请求的数据, 不需要再通过事件监听了.
1.事件监听
app.post("/ajaxPost", function (req, res) {
let allData = "";
req.on("data", function(chunk){
allData += chunk;
});
req.on("end", function(){
console.log(allData);
res.end();
})
});
2.body-parser
// 引入body-parser模块
const bp = require("body-parser");
/*
通过express的服务器对象的use方法, 让所以请求都支持body-parser模块
body-parser在支持请求的时候需要选择 解析的类型
1. bp.urlencoded() 前端post请求过来的数据必须是以参数字符串(键值对)的格式传递的
2. bp.json() 前端post请求过来的数据必须是以json字符串的格式传递的
body-parser以何种方式解析数据,取决于前端以何种格式传数据, 要保持一致
*/
app.use(bp.urlencoded({extended:false}));
app.use(bp.json());
3.监听一个端口号
app.listen(7654, function () {
console.log("服务器开启监听7654端口...");
});
八.以后端为主导的跨域方法—CORS
首先明白,真正阻止跨域请求的是浏览器,而不是服务端
app.all()
all()方法可以监听任意类型的请求, 一般我们用在解决跨域的问题上---CORS
解决跨域的办法:
1. jsonp: 前端主导的解决跨域的办法 思想: html标签没有同源策略
2. CORS(cross-origin-resource-sharing) 跨域资源共享 思想: 设置白名单
CORS(cross-origin-resource-sharing) 跨域资源共享
该方法,完全依靠后端,不需要前端任何操作,CORS是一个服务端协议, 有文档,有规范的标准协议.CORS更加安全.
我们需要捕获每一次请求,来判断是否跨域,或者说是否在我的白名单中,所以我们使用all()方法, 而且捕捉请求的行为要写在最上面
app.all("*", function (req, res, next) {
// 通过设置响应头来给浏览器返回白名单让浏览器将原本跨域的响应放行.
// 设置哪些url可以跨域
res.setHeader("Access-Control-Allow-Origin", "http://localhost:63342");
// 允许我们自定义头部 X-Requested-With表示请求是由ajax发起的, 如果有预检查, 根据触发预检查的情况需要加入对应的自定义头
res.setHeader("Access-Control-Allow-Headers", "X-Requested-With, Content-Type");
// 设置允许响应的请求方式
res.setHeader("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS");
next();
});
完整例子:
xxxx.html
xxxxx.js