1、CORS(跨域资源共享)
1.1、定义
CORS需要浏览器和服务器同时支持,目前,所有浏览器都支持该功能,IE浏览器不能低于IE10
整个CORS通信过程,都是浏览器自动完成,不需要用户参与,对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样,浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉
因此,实现CORS通信的关键是服务器,只要服务器实现了CORS接口,就可以跨源通信
1.2、CORS基本流程
浏览器将CORS请求分成两类: 简单请求(simple request)和非简单请求(not-so-simple request)
浏览器发出CORS简单请求,只需要在头信息之中增加一个Origin字段
浏览器发出CORS非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight),浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段,只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错
1.3、CORS两种请求
只要同时满足以下两大条件,就属于简单请求
1、请求方法是以下三种方法之一:
HEAD
GET
POST
2、HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
是不同时满足上面两个条件,就属于非简单请求
浏览器对这两种请求的处理,是不一样的
* 简单请求和非简单请求的区别
简单请求: 一次请求
非简单请求: 两次请求,在发送数据之前会先发一次请求用于做“预检”,只有“预检”通过后才再发送一次请求用于数据传输
* 关于“预检”
- 请求方式: OPTIONS
- “预检”其实做检查,检查如果通过则允许传输数据,检查不通过则不再发送真正想要发送的消息
- 如何“预检”
(1) 如果复杂请求是PUT等请求,则服务端需要设置允许某请求,否则“预检”不通过
Access-Control-Request-Method
(2) 如果复杂请求设置了请求头,则服务端需要设置允许某请求头,否则“预检”不通过
Access-Control-Request-Headers
支持跨域,简单请求
服务器设置响应头:Access-Control-Allow-Origin = ‘域名’ 或 ‘*’
支持跨域,复杂请求
由于复杂请求时,首先会发送“预检”请求,如果“预检”成功,则发送真实数据。
- “预检”请求时,允许请求方式则需服务器设置响应头:Access-Control-Request-Method
- “预检”请求时,允许请求头则需服务器设置响应头:Access-Control-Request-Headers
1.4、Django项目中支持CORS
1.4.1、在返回的结果中加入允许信息(复杂和简单请求)
def test(request):
if request.method == 'OPTIONS':
# xxx表示你可以在前端的headers头中加入xxx为key的键值对
response['Access-Control-Allow-Headers'] = 'content-type,xxx'
# 处理了简单请求 Access-Control-Allow-Origin写前端项目的地址
# response['Access-Control-Allow-Origin'] = 'http://localhost:8081'
response['Access-Control-Allow-Origin'] = '*'
return response
1.4.2、放到中间件处理复杂和简单请求
1.4.2.1、自定义中间件middle_setting.py
from django.utils.deprecation import MiddlewareMixin
class CorsMiddleware(MiddlewareMixin):
def process_response(self, request, response):
if request.method == 'OPTIONS':
# xxx表示你可以在前端的headers头中加入xxx为key的键值对
response['Access-Control-Allow-Headers'] = 'content-type,xxx'
# 处理了简单请求 Access-Control-Allow-Origin写前端项目的地址
# response['Access-Control-Allow-Origin'] = 'http://localhost:8081'
response['Access-Control-Allow-Origin'] = '*'
return response
1.4.2.2、在setting中注册
# 自己定义处理跨域问题的中间件
MIDDLEWARE = [
...
'middleware_setting.CorsMiddleware',
...
]
2、前端vue处理跨域
http://www.mei.com/silo/women 响应头中Access-Control-Allow-Origin: * 允许所有的域访问
以猫眼电影为例: https://m.maoyan.com/#movie
devServer.proxy
https://cli.vuejs.org/zh/config/ #devserver-proxy
2.1、vue.config.js
module.exports = {
devServer: {
proxy: {
'/ajax': {
target: 'https://m.maoyan.com/',
changeOrigin: true
},
'/user': {
target: 'http://127.0.0.1:8000/',
changeOrigin: true
},
}
}
}
2.2、组件中使用
import axios from 'axios'
// 猫眼
mounted () {
this.$axios.get('ajax/moreClassicList?sortId=1&showType=3').then(res => {
console.log(res.data)
})
}
// user
mounted() {
this.$axios.get('user/test/').then(res => {
console.log(res.data)
})
}
总结(解决跨域问题):
1、前端解决(通过代理解决)
2、后端写代码自行解决(自己写代码解决)
3、借助第三方模块(pip install django-cors-headers)
3、前端主页
3.1、图片准备
将图片放置在项目的img文件夹下
3.2、页头组件: components/Header.vue
<template>
<div class="header">
<div class="slogan">
<p>中国IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
</div>
<div class="nav">
<ul class="left-part">
<li class="logo">
<router-link to="/">
<img src="../assets/img/head-logo.svg" alt="">
</router-link>
</li>
<li class="ele">
<span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
</li>
<li class="ele">
<span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span>
</li>
<li class="ele">
<span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
</li>
</ul>
<div class="right-part">
<div>
<span>登录</span>
<span class="line">|</span>
<span>注册</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data() {
return {
// sessionStorage中有url_path就使用它,没有就是 /
url_path: sessionStorage.url_path || '/',
}
},
methods: {
goPage(url_path) {
// 已经是当前路由就没有必要重新跳转
if (this.url_path !== url_path) {
// js控制路由跳转
this.$router.push(url_path);
}
// 把当前路径加入到了sessionStorage
sessionStorage.url_path = url_path;
},
},
created() {
sessionStorage.url_path = this.$route.path;
this.url_path = this.$route.path;
}
}
</script>
<style scoped>
.header {
background-color: white;
box-shadow: 0 0 5px 0 #aaa;
}
.header:after {
content: "";
display: block;
clear: both;
}
.slogan {
background-color: #eee;
height: 40px;
}
.slogan p {
width: 1200px;
margin: 0 auto;
color: #aaa;
font-size: 13px;
line-height: 40px;
}
.nav {
background-color: white;
user-select: none;
width: 1200px;
margin: 0 auto;
}
.nav ul {
padding: 15px 0;
float: left;
}
.nav ul:after {
clear: both;
content: '';
display: block;
}
.nav ul li {
float: left;
}
.logo {
margin-right: 20px;
}
.ele {
margin: 0 20px;
}
.ele span {
display: block;
font: 15px/36px '微软雅黑';
border-bottom: 2px solid transparent;
cursor: pointer;
}
.ele span:hover {
border-bottom-color: orange;
}
.ele span.active {
color: orange;
border-bottom-color: orange;
}
.right-part {
float: right;
}
.right-part .line {
margin: 0 10px;
}
.right-part span {
line-height: 68px;
cursor: pointer;
}
</style>
3.3、轮播图组件: components/Banner.vue
<template>
<div class="banner">
<el-carousel height="400px">
<el-carousel-item v-for="item in 4" :key="item">
<img src="../assets/img/banner1.png" alt="">
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
export default {
name: "Banner"
}
</script>
<style scoped>
.el-carousel__item {
height: 400px;
min-width: 1200px;
}
.el-carousel__item img {
height: 400px;
margin-left: calc(50% - 1920px / 2);
}
</style>
3.4、页脚组件: components/Footer.vue
<template>
<div class="footer">
<ul>
<li>关于我们</li>
<li>联系我们</li>
<li>商务合作</li>
<li>帮助中心</li>
<li>意见反馈</li>
<li>新手指南</li>
</ul>
<p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p>
</div>
</template>
<script>
export default {
name: "Footer"
}
</script>
<style scoped>
.footer {
width: 100%;
height: 128px;
background: #25292e;
color: #fff;
}
.footer ul {
margin: 0 auto 16px;
padding-top: 38px;
width: 810px;
}
.footer ul li {
float: left;
width: 112px;
margin: 0 10px;
text-align: center;
font-size: 14px;
}
.footer ul::after {
content: "";
display: block;
clear: both;
}
.footer p {
text-align: center;
font-size: 12px;
}
</style>
3.5、主页组件: views/Home.vue
<template>
<div class="home">
<Header />
<Banner />
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<Footer />
</div>
</template>
<script>
import Header from '../components/Header'
import Footer from '../components/Footer'
import Banner from '../components/Banner'
export default {
name: "Home",
components: {
Header,
Footer,
Banner,
}
}
</script>