路飞学城03--首页模块设计和解决跨域问题

1 后台主页模块设计

# 首页需要写的接口分析
    1.轮播图接口 (以它为例)
    2.6个推荐课程接口
    3.老师接口
    
# 建一个首页app:home
    python ../../manage.py startapp home
    
# 轮播图接口
    -轮播图表---》运营从后台把图片上传
        -图片名称,图片地址,跳转链接,描述
        -上传日期,更新日期,图片排序,是否删除(软删除),是否展示
    -迁移(把app注册)
    -查询所有轮播图接口

# 配置开启,media


# 移动端,开启app广告功能(实质就几张录播图片及跳转链接)
    -img:真正的图片
    -link_type:self 或 outer # 跳转类型:内部还是外部 
        # 前端根据这个,自己决定本窗口跳转还是打开新窗口跳转
    -link: /Home 或 http://www.baidu.com  

    后台录入功能:(前后端分离的,需要再写一个广告的录入接口)
        -录入接口  	

1.1 创建基表

from django.db import models
# 只用来继承,不在数据库生成
class BaseModel(models.Model):
    # 上传日期,更新日期,图片排序,是否删除(软删除),是否展示
    created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
    is_show = models.BooleanField(default=True, verbose_name='是否上架')
    orders = models.IntegerField(verbose_name='优先级')
    class Meta:
        abstract=True  #只用来继承,不在数据库生成

1.2 创建home,app,在models下写Banner表

from utils.model import BaseModel
class Banner(BaseModel):
    title = models.CharField(max_length=16, unique=True, verbose_name='名称')
    image = models.ImageField(upload_to='banner', verbose_name='图片')  # 图片的访问地址
    link = models.CharField(max_length=64, verbose_name='跳转链接')
    info = models.TextField(verbose_name='详情')  # 也可以用详情表,宽高出处

    class Meta:
        db_table = 'luffy_banner'
        verbose_name_plural = '轮播图表'

    def __str__(self):
        return self.title
    
    
#### 先注册home,再迁移表

1.3 写查询所有图片接口

# 1 写序列化类
class BannerSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Banner
        fields = ['title','link','image']

        
# 2 写视图类
from . import models
from . import serializer
from django.conf import settings
class BannerView(ViewSetMixin, ListAPIView):
    queryset = models.Banner.objects.all().filter(is_delete=False, is_show=True).order_by('orders')[:settings.BANNER_COUNT]
    serializer_class = serializer.BannerSerializer
    

# 3 配置路径(路由分发)
# 总路由
path('api/v1/home/', include('home.urls')),

# home的app下新建urls.py
router = SimpleRouter()
router.register('banner', views.BannerView, 'banner')
urlpatterns = [
    # path('', include(router.urls)),
]
urlpatterns += router.urls

1.4 自定义用户配置文件

# 在setting文件夹下新建user_setting.py
# 轮播图显示的条数
BANNER_COUNT = 4

# 在项目配置文件dev.py中导入user_setting
from .user_setting import *

1.5 开启media访问

# 配置文件中
# 开启media路径
MEDIA_ROOT=os.path.join(BASE_DIR,'media')  # 开启的路径
MEDIA_URL = '/media/'   # 访问的路径

# 在路由中
    from django.views.static import serve
    # 配置开启media
    # re_path('media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    path('media/<path:path>', serve, {'document_root': settings.MEDIA_ROOT}),
    
    
# 上传文件:传到media文件夹下,表字段的upload_to参数文件夹下

# 访问文件:http://127.0.0.1:8000/media/xx/xx.p

2 跨域问题及解决(*****

# 1  跨域问题出现的原因? PC端网站前后端分离才会出现 (前后端处于不同的端口上 8080和8000)
    -浏览器:同源策略,浏览器的基本安全策略,不允许去不同的url地址获取数据;即域名,端口,协议必须相同
    http://127.0.0.1:8080
    -访问实质是可以的,只是返回的数据被浏览器拦截,不接受而已
            
# 2  CORS:后端技术,跨域资源共享,服务端在响应头中加入一下东西,允许跨域


# 3  CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)

# 4  符合以下条件,都是简单请求
(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
     
# 如果Content-Type是json格式,都是非简单请求

# 区别:简单请求只发一次,非简单请求发送两次,第一次是OPTIONS预检请求,第二次是真正的请求


# 5 后端使用cors处理跨域问题
    # 5.1 简单请求,支持跨域
	-在响应头中设置:Access-Control-Allow-Origin = '域名' 或 '*'

    # 5.2 非简单请求支持跨域
	-在响应头中设置:Access-Control-Allow-Methods='*'
	-在响应头中设置:Access-Control-Allow-Headers='*'

    # 自己处理跨域,跟web框架无关
    # 写一个中间件
    class CorsMiddle(MiddlewareMixin):
        def process_response(self, request, response):
            # 处理非简单请求
            if request.method == 'OPTIONS':
                response['Access-Control-Allow-Headers'] = '*'
                response['Access-Control-Allow-Methods'] = '*'
            # 处理简单请求
            response['Access-Control-Allow-Origin'] = '*'
            return response
    # 中间件配置
   
    
# 6 使用第三方,解决跨域
    1 安装
    	pip install django-cors-headers
    2 在app中注册
    	INSTALLED_APPS = (
            ...
            'corsheaders',
            ...
        )
    3 在中间件中加入
        MIDDLEWARE = [  
            ...
            'corsheaders.middleware.CorsMiddleware',
            ...
        ]
    
    4 在配置文件中配置
        CORS_ALLOW_CREDENTIALS = True  # 允许携带Cookie
	CORS_ORIGIN_ALLOW_ALL = True  # 允许所有域
	CORS_ORIGIN_WHITELIST = (  # 允许域的白名单 
    	    'http://localhost:8080',  
            # django-cors-headers版本影响白名单写法,版本包小于3.0不用加http,版本包大于3.0则需要加http
	)

        CORS_ALLOW_METHODS = (
            'DELETE',
            'GET',
            'OPTIONS',
            'PATCH',
            'POST',
            'PUT',
            'VIEW',
        )

        CORS_ALLOW_HEADERS = (
            'XMLHttpRequest',
            'X_FILENAME',
            'accept-encoding',
            'authorization',
            'content-type',
            'dnt',
            'origin',
            'user-agent',
            'x-csrftoken',
            'x-requested-with',
            'Pragma',
            'token',
        )

3 simple-ui后台管理

# 项目:
    -主站:互联网用户,前后端分离
    -后台管理:运营,领导...,前后端混合,使用admin
    	-simple-ui 美化admin
        
# 集成simpleui
# 创建超级用户
    python manage.py createsuperuser
    
# 录入数据

# 了解:simple-ui是vue+elementui开发的
    -基于simpleui二次开发

4 路飞前台首页

# 三个组件:头部,轮播图,尾部
# Home组件中引入
# Banner轮播图和后台接口对接完成,实现跳转


# 前端存数据的地方:
    # 1.cookie:有过期时间,借助第三方 vue-cookies
    	.set(key,value)
        .get(key)
        
    # 2.sessionStorage:临时存储,页面关闭就没了 
    	sessionStorage.name = 'lqz';
        
    # 3.localStorage:永久存储,除非客户清理
    	localStorage.age='19'

4.1 Footer组件

<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>

4.2 Banner组件

<template>
    <div class="banner">
        <el-carousel height="400px">
            <el-carousel-item v-for="item in banner_list" :key="item">
                <div v-if="item.link.startsWith('http')">
                    <a :href="item.link"><img :src="item.image" :alt="item.title"></a>
                </div>
                <div v-else>
                    <router-link :to="item.link"><img :src="item.image" :alt="item.title"></router-link>
                </div>
            </el-carousel-item>
        </el-carousel>
    </div>
</template>

<script>
    export default {
        name: "Header",
        data() {
            return {
                banner_list: []
            }
        },
        created() {
            this.$http.get(this.$settings.base_url + 'home/banner/').then(res => {
                this.banner_list = res.data
            })
        }
    }
</script>

<style scoped>
    .el-carousel__item {
        height: 400px;
        min-width: 1200px;
    }

    .el-carousel__item img {
        height: 400px;
        margin-left: calc(50% - 1920px / 2);
    }
</style>

4.3 Header组件

<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 {
                url_path: sessionStorage.url_path || '/',
            }
        },
        methods: {
            goPage(url_path) {
                // 已经是当前路由就没有必要重新跳转
                if (this.url_path !== url_path) {
                    this.$router.push(url_path);
                }
                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>

补充

1.xss、csrf、cors的区别

# xss: 跨站脚本攻击
    恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页面时,
    嵌入Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。

# csrf: 跨站请求伪造
    解决csrf攻击:服务器会给网站返回一个隐藏的随机字符串,第三方网站并不能获取到该字符串,
    请求来时,服务器会验证cookies以及该字符串(证明是由正确的网站发的请求)

# cors: 跨域资源共享
    是浏览器的同源策略,浏览器的基本安全策略,不允许去不同的url地址获取数据;即域名,端口,协议必须相同

posted @ 2022-02-10 23:48  Edmond辉仔  阅读(114)  评论(0编辑  收藏  举报