zoukankan      html  css  js  c++  java
  • 博客项目——〇六 添加新文章——富文本编辑器、beautifulsoup的使用

    我们前面在试各种功能的时候都是在admin下把数据硬插在table里,但是这明显不符合我们的日常使用环境,博客里最常用的就是添加新的文章了,所以这里我们就看一看文章的添加是怎么实现的。

    富文本编辑器

     看一看博客园在添加文章的页面,新的文章是怎么添加进去的呢?

     这个图片里的文本编辑器就是一个富文本编辑器(富文本编辑器,Rich Text Editor, 简称 RTE, 是一种可内嵌于浏览器,所见即所得的文本编辑器。)因为我们在编辑文本的时候包含了各种各样的样式,其实真实情况下我们是写了一个html的代码,这个代码点击上面的图标是可以显示出来的,只不过富文本编辑器是一种所见即所得的效果,更利于我们的编辑。但是最终生成的还是一堆html类型的字符串。

    kindeditor

    在新文章的页面中我们就使用了KindEditor这个富文本编辑器,点击官网查看,下载以后放在static文件夹中,就可以直接调用了,在HTML页面中就是定义一个textarea的标签,然后导入Kindeditor,把他绑定给这个textarea

    <textarea id="editor_id" name="content" style="700px;height:300px;">
    &lt;strong&gt;HTML内容&lt;/strong&gt;
    </textarea>
    
    <script charset="utf-8" src="/editor/kindeditor.js"></script>
    <script charset="utf-8" src="/editor/lang/zh-CN.js"></script>
    <script>
            KindEditor.ready(function(K) {
                    window.editor = K.create('#editor_id');
            });
    </script>

    至于create函数中还可以添加好多参数,可以直接在官网上查到。

    官网上的文档说的很清楚,如何在页面中加载KindEditor,这里就不说了。但是下面的一个用法是要着重讲一下的:图片的上传。

    KindEditor中上传图片

     默认的KindEditor是有两个图片上传的按钮的,但是上传以后是没有任何行为的(我们也没有告诉他上传到服务器的哪里?怎么上传)!所以我么就要给K.create()中添加下面几个参数

    <script>
        KindEditor.ready(function(K){
            window.editor = K.create('#editor_id',{
                uploadJson:'/upload/',
        })
    </script>

    就是指定了上传图片时候对应的url请求。然后上传一下会发现有下面的错误

     很明显,发生了跨站请求错误,所以要添加csrf参数,这个参数的添加在一个新的配置参数中

    1 uploadJson:'/upload/',
    2 extraFileUploadParams:{
    3     csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
    4 },
    5 filePostName:"upload_img"   //指定上传文件的key

    但是一定要记得在前面加上{%csrf_token%}才行啊,要不是jQuery取不到这个csrf的值。

    最后一行的filePostName的作用我们下面马上会说明。

    既然上传图片是用到一个url,所以我们要在路由总设置好这个url和它对应的视图函数

     1 def upload(request):
     2     print(request.FILES)
     3     obj = request.FILES.get('upload_img')
     4     path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name)
     5     print(obj.name)
     6 
     7     with open(path,'wb') as f:
     8         for line in obj:
     9             f.write(line)
    10     return HttpResponse('OK')

    我们在settings.py中定义了一个静态文件的上传路径(/media/)为了管理的方便,我们在这个路径下新建一个img/文件夹来存放上传的图片文件。所以path就是项目的路径加上img后面是文件名。而request.FILE.get的值就是刚刚我们在模板中定义的filePostName

    最后用常规的文件句柄把数据写入文件就行了。

    上传完毕,就有下面的效果

     不要看标题上的上传错误,那个是定义出来的显示内容。主体内的OK是我们在视图中通过HttpResponse返回的字符串。然后在看看media文件夹内,是不是有了我们新上传的图片!

    上传图片的显示

    在常规操作中,我们一点上传图片的时候在文本编辑框中会出现我们上传的图片,这个操作就是需要我们视图中加一个返回的字典

     1 def upload(request):
     2     obj = request.FILES.get('upload_img')
     3     path = os.path.join(settings.MEDIA_ROOT,'img/',obj.name)
     4 
     5     with open(path,'wb') as f:
     6         for line in obj:
     7             f.write(line)
     8 
     9     ret = {
    10         'error':0,
    11         'url':'/media/img/'+obj.name,
    12     }
    13 
    14     return HttpResponse(json.dumps(ret))

    我们返回了一个字典,里面是一个状态字和一个url,这个url就是我们保存图片的路径,一定要注意,这个路径不是绝对路径!这样就完成了上传图片的显示

    富文本编辑器里内容的保存

     新的文章保存,有一个字段比较重要:desc,我们在访问博客园的时候会看到有个这样的文章简介,就是标题下面那个

     这个简介就是把文章的内容截取了一个固定的长度,我们可以先用切片的方法来试一下

     1 def new_article(request,username):
     2     if request.method == 'POST':
     3         title = request.POST.get('title')
     4         content = request.POST.get('article_content')
     5 
     6         user = request.user
     7 
     8         desc = content[:150]
     9 
    10         models.Article.objects.create(user=user,desc=desc)
    11         print(title,content)
    12 
    13     return render(request,'new_article.html')

    结果会是这种显示效果

     这是因为我们从页面的富文本编辑器传过来的字符串是一堆HTML形式的代码,字符串在做切片的时候是按照整个HTML文件作为一个大字符串来切片的,图示中的效果还算好,有些时候可能由于文章中的一些特定的标签在切片的时候被切掉,导致整个页面的效果崩溃。这就要用到一个新的库——BeautifulSoup。我们可以先通过下面的案例看一看这个beautifulsoup的作用是什么

    from bs4 import BeautifulSoup
    s = "<div>BeautifulSoup测试</div>"
    soup = BeautifulSoup(s,'html.parser')
    print('soup:\n',soup)
    print('soup.text:\n',soup.text)
    
    ##########输出##########
    soup:
     <div>BeautifulSoup测试</div>
    soup.text:
     BeautifulSoup测试

    我们可以通过soup.text获取到各种标签之间的字符串,就是页面上显示的内容。所以就可以把上面的那段视图代码修改成下面的方式

     1 from bs4 import BeautifulSoup
     2 def new_article(request,username):
     3     if request.method == 'POST':
     4         title = request.POST.get('title')
     5         content = request.POST.get('article_content')
     6 
     7         soup = BeautifulSoup(content,'html.parser')
     8         user = request.user
     9 
    10         article_obj = models.Article.objects.create(user=user,desc=soup.text[0:150],title=title)
    11         models.ArticleDetail.objects.create(content = content,article = article_obj)
    12         print(title,content)
    13 
    14     return render(request,'new_article.html')
    简易的XSS攻击预防

    XSS攻击

     我们在前面提到过XSS攻击,这里可以再演示一下:如果我们新写的文章是这样的:

     注意,添加的字符串类型选择的是html,那么我们在打开这个文章页面的时候就会有个弹框弹出来

     这还是最简单的alert攻击,如果加上了死循环什么的浏览器就崩溃了。

    看一下百度百科对XSS攻击的定义:

    XSS攻击通常指的是通过利用网页开发时留下的漏洞,通过巧妙的方法注入恶意指令代码到网页,使用户加载并执行攻击者恶意制造的网页程序。这些恶意网页程序通常是JavaScript,但实际上也可以包括Java、 VBScript、ActiveX、 Flash 或者甚至是普通的HTML。攻击成功后,攻击者可能得到包括但不限于更高的权限(如执行一些操作)、私密网页内容、会话和cookie等各种内容

    简单的XSS防御

    有一个最简单暴力的方法就是对整个添加的字符串进行正则搜索,把定义的关键字率除掉,就可以了。但是BeautifulSoup给我们提供了一个功能,对指定的标签类型进行过滤

    from bs4 import BeautifulSoup
    s = "<div>BeautifulSoup测试</div> \
        <a href='http://127.0.0.1:8000'>a标签</a> \
            <script> \
            alert('XSS攻击测试')\
            </script>"
    
    soup = BeautifulSoup(s,'html.parser')
    
    for tag in soup.find_all():
        if tag.name in ['script','a']:
            tag.decompose()
    
    print(soup)
    
    ##########输出值##########
    <div>BeautifulSoup测试</div>  

    可以看一下,我们可以用find_all的方式来拿到所有的tag,如果这个tag的样式是我们指定的(script和a标签),直接删掉。不是的就保留下来。

    也就是我们直接定义一个非法的标签列表就行了。

    但是还是有一点问题,像博客园一样,我们是可以插入程序段,里面包含了script的类型,那么用这个方式就被过滤掉了,留个悬念,以后再讲!

  • 相关阅读:
    ASP.NET Core应用程序容器化、持续集成与Kubernetes集群部署(二)
    ASP.NET Core应用程序容器化、持续集成与Kubernetes集群部署(一)
    2018年9月15日上海MVP线下技术交流活动简报
    在.NET中使用机器学习API(ML.NET)实现化学分子式数据格式的判定
    使用Roslyn的C#语言服务实现UML类图的自动生成
    ASP.NET Core应用程序的参数配置及使用
    微软最有价值专家(Microsoft MVP)项目经验分享
    漫谈单体架构与微服务架构(上):单体架构
    ASP.NET Core Web API下事件驱动型架构的实现(五):在微服务中使用自我监听模式保证数据库更新与消息派发的可靠性
    使用C#读写结构化的二进制文件
  • 原文地址:https://www.cnblogs.com/yinsedeyinse/p/13190812.html
Copyright © 2011-2022 走看看