ajax和跨域方案最全总结
ajax和跨域方案总结
HTTP协议
超文本传输协议(Hypertext Transfer Protocol
,简称HTTP
)是应用层协议。HTTP
是一种请求/响应式的协议,即一个客户端与服务器建立连接后,向服务器发送一个请求;服务器接到请求后,给予相应的响应信息
HTTP请求报文
HTTP 请求报文由请求行
、请求头部
、空行
和 请求包体
4 个部分组成
请求行
请求行由请求方法字段
、URL字段
和HTTP协议版本
字段3个字段组成,它们用空格分隔如:
GET /index.html HTTP/1.1
请求方法
常用的 HTTP
请求方法有 GET
、POST
、HEAD
、PUT
、DELETE
、OPTIONS
、TRACE
、CONNECT
请求头部
请求头部由(关键字:<空格>值)对组成,每行一对,关键字和值用英文冒号“:<空格>”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:
Accept-Charset
:可接受的应答的字符集Host
:请求的主机名,允许多个域名同处一个IP
地址,即虚拟主机Cookie
:存储于客户端扩展字段,向同一域名的服务端发送属于该域的cookie
空行
最后一个请求头之后是一个空行,发送回车符和换行符,通知服务器以下不再有请求头
请求包体
请求包体不在 GET
方法中使用,而是在POST
方法中使用。POST
方法适用于需要客户填写表单的场合。与请求包体相关的最常使用的是包体类型 Content-Type
和包体长度 Content-Length
HTTP 响应报文
HTTP 响应报文由状态行
、响应头部
、空行
和 响应包体
4 个部分组成
状态行
状态行由 HTTP 协议版本字段
、状态码
和状态码
的描述文本 3 个部分组成,他们之间使用空格隔开,例如
HTTP/1.1 200 OK
状态码
状态码由三位数字组成,第一位数字表示响应的类型,常用的状态码有五大类如下所示:
2xx
:表示服务器已成功接收到请求并进行处理;3xx
:表示服务器要求客户端重定向;4xx
:表示客户端的请求有非法内容;5xx
:表示服务器未能正常处理客户端的请求而出现意外错误;
常见的状态码务必要熟悉:
200 OK
:表示客户端请求成功;400 Bad Request
:表示客户端请求有语法错误,不能被服务器所理解;401 Unauthonzed
:表示请求未经授权,该状态代码必须与 WWW-Authenticate 报头域一起使用;403 Forbidden
:表示服务器收到请求,但是拒绝提供服务,通常会在响应正文中给出不提供服务的原因;404 Not Found
:请求的资源不存在,例如,输入了错误的URL;500 Internal Server Error
:表示服务器发生不可预期的错误,导致无法完成客户端的请求;503 Service Unavailable
:表示服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常;
响应头部
响应头可能包括:
Location
:Location响应报头域用于重定向接受者到一个新的位置。例如:客户端所请求的页面已不存在原先的位置,为了让客户端重定向到这个页面新的位置,服务器端可以发回Location响应报头后使用重定向语句,让客户端去访问新的域名所对应的服务器上的资源。这个头通常配合302重定向状态码使用。浏览器接收到这样的响应信息后,通常会立刻访问Location头所指向的页面Set-Cookie
:向客户端设置Cookie。与Cookie请求头相互对应。Set-Cookie头是服务器向客户端设置Cookie,Cookie头是客户端向服务器传客户端已经保存的Cookie信息
空行
最后一个响应头部之后是一个空行,发送回车符和换行符,通知服务器以下不再有响应头部
响应包体
服务器返回给客户端的文本信息
示例
ajax原理
//创建ajax对象
var xhr = new XMLHttpRequest();
//告诉 Ajax 请求地址以及请求方式
xhr.open('get', 'http://www.example.com');
//发送请求
xhr.send();
//获取服务器端给与客户端的响应数据
xhr.onload = function () {
console.log(xhr.responseText);
}
post提交
格式1-urlencoded
数据格式为字符串,属性名1=属性值1&属性名2=属性值2
原生
// 通过请求头告诉后端数据格式
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
// 发送数据
xhr.send('uname=zs&age=20');
jQuery
$.ajax({
type: 'post',
url: 'http://www.example.com',
data: {
uname: 'zs',
age: 20
},
contentType: "application/x-www-form-urlencoded", //默认的提交方式,当不写contentType时即是此种方式
success(data){
}
})
axios
const data = new URLSearchParams();
data.append('uname', 'zs');
data.append('age', 20);
axios({
url: 'http://www.example.com',
method: 'post',
data
})
格式2-json
数据格式为字符串,{"属性名1":"数字","属性名2":数字}
原生
// 通过请求头告诉后端数据格式
xhr.xhr.setRequestHeader("Content-Type", "application/json")
// 发送数据
xhr.send('{"uname": "zs", "age": 20}');
jQuery
$.ajax({
type: 'post',
url: 'http://www.example.com',
data: JSON.stringify({
uname: 'zs',
age: 20
}),
contentType: "application/json", //默认的提交方式,当不写contentType时即是此种方式
success(data){
}
})
axios
axios({
url: 'http://www.example.com',
method: 'post',
data: {
uname: 'zs',
age: 20
}
})
格式3-form-data
数据格式为表单提交对象
原生
// 用FormData对象存储数据
var formData = new FormData()
formData.append("uname", "zs")
formData.append("age": 20)
// 发送数据
xhr.send(formData);
jQuery
var formData = new FormData()
formData.append("uname", "zs")
formData.append("age": 20)
$.ajax({
type: 'post',
url: 'http://www.example.com',
data: formData,
contentType: false,// 当有文件要上传时,此项是必须的,否则后台无法识别文件流的起始位置
processData: false,// 是否序列化data属性,默认true
success(data){
}
})
axios
var formData = new FormData()
formData.append("uname", "zs")
formData.append("age": 20)
axios({
url: 'http://www.example.com',
method: 'post',
data: formData
})
get提交
格式1-urlencoded
数据通过地址栏发送,数据格式为字符串,属性名1=属性值1&属性名2=属性值2
原生
xhr.open('get', 'http://www.example.com?uname=zs&age=20');
jQuery
$.ajax({
type: 'get',
url: 'http://www.example.com',
data: {
uname: 'zs',
age: 20
},
success(data){
}
})
axios
axios({
url: 'http://www.example.com',
method: 'get',
params: {
uname: 'zs',
age: 20
}
})
格式2-restful
原生
xhr.open('get', 'http://www.example.com/user/1');
jQuery
$.ajax({
type: 'get',
url: 'http://www.example.com/user/1',
success(data){
}
})
axios
axios({
url: 'http://www.example.com/user/1',
method: 'get'
})
vue当中使用axios
简化写法
例如: 在main.js
中
import axios from 'axios'
import Vue from 'vue'
import router from './router/index.js'
// axios的基准地址
axios.defaults.baseURL = 'http://www.example.com'
// axios的请求拦截器添加token
axios.interceptors.request.use(function (config) {
// 如果有token,就发送
let token = localStorage.getItem('token')
if(token){
config.headers.Authorization = token
}
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// axios的响应拦截器判断token是否失效
axios.interceptors.response.use(function (response) {
// 通过响应体返回token失效的状态码
if(response && response.data && response.data.code === 401){
router.push('/login')
return Promise.reject(new Error('token失效'));
}
return response;
}, function (error) {
// 通过响应头返回token失效的状态
if(error.response && error.response.status === 401){
router.push('/login')
}
return Promise.reject(error);
});
//将axios挂载到Vue的原型链上
Vue.prototype.$http = axios
在组件中
<template>
<div></div>
</template>
<script>
export default {
async create(){
let res = await this.$http.post('/user', {
uname: 'zs',
age: 20
})
}
}
</script>
模块化写法
例如: 在util/request.js
中
import axios from 'axios'
const service = axios.create({
baseURL: 'http://www.example.com'
})
// service的请求拦截器添加token
service.interceptors.request.use(function (config) {
// 如果有token,就发送
let token = localStorage.getItem('token')
if(token){
config.headers.Authorization = token
}
return config;
}, function (error) {
// 对请求错误做些什么
return Promise.reject(error);
});
// service的响应拦截器判断token是否失效
service.interceptors.response.use(function (response) {
// 通过响应体返回token失效的状态码
if(response && response.data && response.data.code === 401){
router.push('/login')
return Promise.reject(new Error('token失效'));
}
return response;
}, function (error) {
// 通过响应头返回token失效的状态
if(error.response && error.response.status === 401){
router.push('/login')
}
return Promise.reject(error);
});
export default service
在api/xxx.js
中
import request from '@/util/request.js'
export function addUser(data){
return request({
url: '/user',
method: 'post',
data
})
}
在组件中
<template>
<div></div>
</template>
<script>
import {addUser} from '@/api/xxx.js'
export default {
async create(){
let res = await addUser({
uname: 'zs',
age: 20
})
}
}
</script>
跨域
什么是同源政策
如果两个页面拥有相同的协议、域名和端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源
同源政策的目的
同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据。最初的同源政策是指 A 网站在客户端设置的Cookie,B网站是不能访问的。
随着互联网的发展,同源政策也越来越严格,在不同源的情况下,其中有一项规定就是无法向非同源地址发送Ajax 请求,如果请求,浏览器就会报错。
方案1-jsonp
前端
import jsonp from 'jsonp'
import querystring from 'querystring'
jsonp('http://www.example.com/user?'+querystring.encode({
uname: 'zs',
age: 20
}),
function (err, data) {
})
后端
import express from 'express'
const app = express()
app.get('/user',(req, res)=>{
// 获取数据
req.query
// 返回数据
res.jsonp({data: 'ok'})
})
app.lisiten(80,()=>{})
方案2-cros
CROS
:全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制。
前端
import axios from 'axios'
axios({
url: 'http://www.example.com/user',
method: 'post',
data: {
uname: 'zs',
age: 20
}
})
后端
import express from 'express'
import cros from 'cros'
const app = express()
//通过json中间实现接受数据
app.use(express.json())
//通过cros中间件实现跨域
app.use(cros())
/*
//上面语句等同于下面语句
app.use((req,res)=>{
// 通过响应头返回
// (1)请求的来源的网址为任意网址
res.header('Access-Control-Allow-Origin', '*')
// (2)请求头里可以传的数据
res.header('Access-Control-Allow-Headers', 'Content-Type,Authorization')
// (3)请求可以用的请求方式
res.header('Access-Control-Allow-Methods', 'PUT,DELETE,POST,GET')
})
*/
app.get('/user',(req, res)=>{
// 获取数据
req.body
// 返回数据
res.send({data: 'ok'})
})
app.lisiten(80,()=>{})
服务器代理
在vue脚手架项目中
在util/request.js
中,例如:
import axios from 'axios'
const request = axios.create({
baseURL: '/api'
})
export default request
在api/xxx.js
中,例如:
import request from '@/util/request.js'
export function addUser(data){
return request({
url: '/user',
method: 'post',
data
})
}
在vue.config.js
中,例如:
module.exports = {
devServer: {
proxy: {
//拦截地址
'/api': {
//目标地址
target: 'http://www.example.com',
//改变请求头中的origin为target目录
changeOrigin: true,
//路径重写
pathRewrite: {
//替换路径中的/api的为''
'^/api' : ''
}
}
}
}
}
转载自:https://juejin.cn/post/7142482520062296101