個人第一個python web開發框架(15)——公司介紹編輯功能

  完成登陸之後,就會進入後臺管理系統的主界面,由於這個是小項目,因此導航菜單所有固化在HTML中,不能修改。通常後臺還會有一個歡迎頁或關鍵數據展現的主頁面,小項目也沒有多大的必要,因此登陸後直接進入公司介紹編輯頁面。javascript

  首先咱們來看一下公司介紹頁面內容html

  

  看上去功能好像很簡單,其實咱們要處理的東西仍是挺多的。前端

  從頁面上看,咱們須要有一個記錄讀取的接口,來獲取公司介紹的內容,並展現在頁面上。固然如今數據庫裏面沒有記錄存在,因此咱們還須要向數據庫的信息表(infomation)中插入一條公司介紹的記錄,這樣好直接進行編輯(由於公司介紹不會有不少條記錄,通常定了後就不會再改變,因此只須要在數據庫的信息表裏插入一條就能夠了)java

  另外,從界面上看,咱們還須要有一個上傳文件的接口,能夠上傳圖片和文件;還須要一個更新公司介紹內容的接口。python

  還有須要修改幾個地方,有上傳文件,確定須要有下載的接口,因此須要增長一個下載的路由(python與其餘語言不同的地方是,全部訪問都必須經過路由,因此上傳的或放在目錄中的文件須要統必定義一個接口來處理,否則用戶訪問不了,雖然有點麻煩,但這樣處理也安全不少,用戶上傳任何含有木馬或程序的文件,它也沒法在服務器端執行);nginx配置文件也須要修改一下,增長下載路徑規則,這樣就能夠直接經過nginx訪問下載路徑了。nginx

 

  向數據庫中添加公司介紹記錄web

  運行pgAdmin連上數據庫,而後按第4章的作法,打開sql查詢分析器,運行下面代碼添加一條數據庫記錄sql

INSERT INTO infomation(id, title)  VALUES (1, '公司介紹');

 

  添加公司介紹記錄讀取接口數據庫

 1 @get('/api/about/')
 2 def callback():
 3     """
 4     獲取指定記錄
 5     """
 6     sql = """select * from infomation where id = 1"""
 7     # 讀取記錄
 8     result = db_helper.read(sql)
 9     if result:
10         # 直接輸出json
11         return web_helper.return_msg(0, '成功', result[0])
12     else:
13         return web_helper.return_msg(-1, "查詢失敗")

  由於公司介紹id添加後不會再改變,因此sql語句直接綁死id爲1,另外,執行數據庫查詢之後,返回的是列表,因此返回記錄時要加上序號:result[0]json

  啓動debug(對main.py點擊右鍵=》debug),將用戶登陸判斷那兩行註釋掉(否則直接訪問會返回-404,「您的登陸已失效,請從新登陸」提示),在瀏覽器輸入:http://127.0.0.1:9090/api/about/就能夠看到返回結果(結果的中文字符是unicode編碼,須要用站長工具轉換一下才能夠轉爲下載效果)

{"msg": "成功", "data": {"content": "", "front_cover_img": "", "id": 1, "title": "公司介紹", "add_time": "2017-10-31 14:17:45"}, "state": 0}

 

  添加公司介紹內容修改接口

 1 @put('/api/about/')
 2 def callback():
 3     """
 4     修改記錄
 5     """
 6     front_cover_img = web_helper.get_form('front_cover_img', '圖片')
 7     content = web_helper.get_form('content', '內容', is_check_special_char=False)
 8     # 防sql注入攻擊處理
 9     content = string_helper.filter_str(content, "'")
10     # 防xss攻擊處理
11     content = string_helper.clear_xss(content)
12 
13     # 更新記錄
14     sql = """update infomation set front_cover_img=%s, content=%s where id=1"""
15     vars = (front_cover_img, content,)
16     # 寫入數據庫
17     db_helper.write(sql, vars)
18 
19     # 直接輸出json
20     return web_helper.return_msg(0, '成功')

  由於公司介紹只須要一條記錄就夠了,前面使用手動方式向數據庫添加記錄,因此代碼中咱們就不須要寫添加的方法

  修改記錄使用put方式接收:@put('/api/about/')

  從界面圖片中能夠看到,有文章標題、首頁圖片和文章內容,由於標題不須要進行修改,因此咱們修改接口只須要處理剩下兩項就能夠了。

  由於提交的內容含有HTML代碼,因此使用web_helper.get_form提取值時,須要使用is_check_special_char參數,設置爲不檢查特殊符號,否則會接收不了。另外接收到參數值之後,咱們須要對它進行防sql注入和防xss處理。

  clear_xss()函數是string_helper包新增的清除xss攻擊標籤用的,它會過濾掉xss的攻擊代碼。詳細代碼以下:

def clear_xss(html):
    """
    清除xss攻擊標籤
    :param html: 要處理的html
    :return:
    """
    tags = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'strong', 'ul']
    tags.extend(
        ['div', 'p', 'hr', 'br', 'pre', 'code', 'span', 'h1', 'h2', 'h3', 'h4', 'h5', 'del', 'dl', 'img', 'sub', 'sup', 'u',
         'table', 'thead', 'tr', 'th', 'td', 'tbody', 'dd', 'caption', 'blockquote', 'section'])
    attributes = {'*': ['class', 'id'], 'a': ['href', 'title', 'target'], 'img': ['src', 'style', 'width', 'height']}
    return bleach.linkify(bleach.clean(html, tags=tags, attributes=attributes))

  clear_xss()函數中咱們使用了bleach這個庫(須要安裝:pip install bleach),它是一個基於白名單、經過轉義或去除標籤和屬性的方式,來對HTML文本淨化的python庫。

  咱們在string_helper_test.py這個測試單元中添加一個測試用例,來測試一下這個函數的使用效果

    def test_clear_xss(self):
        print('-----test_clear_xss------')
        print(string_helper.clear_xss('<script src="javascript:alert(1);">abc</script>'))
        print(string_helper.clear_xss('<iframe src="javascript:alert(1);">abc</iframe>'))
        print(string_helper.clear_xss('<div style="width:0;height:0;background:url(javascript:document.body.onload = function(){alert(/XSS/);};">div</div>'))
        print(string_helper.clear_xss('<img src = "#"/**/onerror = alert(/XSS/)>'))
        print(string_helper.clear_xss('<img src = j ava script:al er t(/XSS/)>'))
        print(string_helper.clear_xss("""<img src = j
ava script :a ler t(/xss/)>"""))
        print(string_helper.clear_xss('<img src="javacript:alert(\'abc\')"></img>'))
        print(string_helper.clear_xss('<img src="https://www.baidu.com/img/baidu_jgylogo3.gif"></img>'))
        print(string_helper.clear_xss('<p src="javascript:alert(1);">abc</p>'))
        print(string_helper.clear_xss("""<input type="text" value="琅琊榜" onclick="javascript:alert('handsome boy')">"""))
        print(string_helper.clear_xss('<p onclick="javascript:alert("handsome boy")>abc</p>'))
        print(string_helper.clear_xss('<a href="javascript:alert(1);">abc</a>'))
        print(string_helper.clear_xss('<a href="/api/">abc</a>'))
        print(string_helper.clear_xss('<a href="http://www.baidu.com">abc</a>'))
        print(string_helper.clear_xss('<marquee onstart="alert(/XSS/)">文字</marquee>'))
        print(string_helper.clear_xss('<div style="" onmouseenter="alert(/XSS/)">文字</div>'))
        print(string_helper.clear_xss('<li style = "TEST:e-xpression(alert(/XSS/))"></li>'))
        print(string_helper.clear_xss('<input id = 1 type = "text" value="" <script>alert(/XSS/)</script>"/>'))
        print(string_helper.clear_xss('<base href="http://www.labsecurity.org"/>'))
        print(string_helper.clear_xss('<div id="x">alert%28document.cookie%29%3B</div>'))
        print(string_helper.clear_xss('<limited_xss_point>eval(unescape(x.innerHTML));</limited_xss_point>'))

  執行後輸出結果:

------ini------
-----test_clear_xss------
&lt;script src="javascript:alert(1);"&gt;abc&lt;/script&gt;
&lt;iframe src="javascript:alert(1);"&gt;abc&lt;/iframe&gt;
<div>div</div>
<img src="#">
<img src="j">
<img src="j">
<img>
<img src="https://www.baidu.com/img/baidu_jgylogo3.gif">
<p>abc</p>
&lt;input onclick="javascript:alert('handsome boy')" type="text" value="琅琊榜"&gt;
<p>abc</p>
<a>abc</a>
<a href="/api/" rel="nofollow">abc</a>
<a href="http://www.baidu.com" rel="nofollow">abc</a>
&lt;marquee onstart="alert(/XSS/)"&gt;文字&lt;/marquee&gt;
<div>文字</div>
<li></li>
&lt;input &lt;script="" id="1" type="text" value=""&gt;alert(/XSS/)"/&gt;
&lt;base href="<a href="http://www.labsecurity.org" rel="nofollow">http://www.labsecurity.org</a>"&gt;
<div id="x">alert%28document.cookie%29%3B</div>
&lt;limited_xss_point&gt;eval(unescape(x.innerHTML));&lt;/limited_xss_point&gt;
------clear------

  能夠看到,對於富文本編輯器提交的代碼,bleach基本知足了咱們的防範xss攻擊的處理需求

 

  添加上傳接口(PS:咱們使用的文本編輯器是百度的ueditor,由於它沒有python的上傳處理代碼,因此咱們須要動手編輯上傳接口,以及html上也要進行對應的修改)

#!/usr/bin/evn python
# coding=utf-8

import os
from bottle import post, request
from common import datetime_helper, random_helper, log_helper

@post('/api/files/')
def callback():
    """
    修改記錄
    """
    # 初始化輸出值
    result = {
        "state": "FAIL",
        "url": "",
        "title": "上傳失敗",
        "original": ""
    }
    # 獲取上傳文件
    try:
        # upfile爲前端HTML上傳控件名稱
        upload = request.files.get('upfile')
        # 若是沒有讀取到上傳文件或上傳文件的方式不正確,則返回上傳失敗狀態
        if not upload:
            return result

        # 取出文件的名字和後綴
        name, ext = os.path.splitext(upload.filename)
        # 給上傳的文件重命名,默認上傳的是圖片
        if ext and ext != '':
            file_name = datetime_helper.to_number() + random_helper.get_string(5) + ext
        else:
            file_name = datetime_helper.to_number() + random_helper.get_string(5) + '.jpg'
        upload.filename = file_name

        # 設置文件存儲的相對路徑
        filepath = '/upload/' + datetime_helper.to_number('%Y%m%d') + '/'
        # 組合成服務器端存儲絕對路徑
        upload_path = os.getcwd() + filepath
        # 若是目錄不存在,則建立目錄
        if not os.path.exists(upload_path):
            os.mkdir(upload_path)
        # 保存文件
        upload.save(upload_path + upload.filename, overwrite=True)

        # 設置輸出參數(返回相對路徑給客戶端)
        result['title'] = result['original'] = upload.filename
        result['url'] = filepath + upload.filename
        result['state'] = 'SUCCESS'
    except Exception as e:
        log_helper.error('上傳失敗:' + str(e.args))

    # 直接輸出json
    return result

  PS:這裏只作了上傳文件處理,沒有上傳成功之後存儲到數據庫中統一管理,若是前端反覆上傳,會形成服務器存儲不少多餘文件的問題,你們能夠本身發揮想象與動手能力,看看怎麼解決這個問題。對於這個問題會在第二部分統一處理。

 

  添加上傳文件存儲文件夾:直接在項目的要目錄下建立upload文件夾

  

 

  修改main.py文件配置,並建立文件下載路由

  導入的bottle庫添加response, static_file這兩個包,response用於設置輸出文件類型爲二進制數據傳輸格式,這樣設置後,上傳的各類類型文件均可如下載;static_file是使用安全的方式讀取文件並輸出到客戶端

from bottle import default_app, get, run, request, hook, route, response, static_file

  在第26行插入下面代碼,初始化上傳文件存儲路徑

# 定義upload爲上傳文件存儲路徑
upload_path = os.path.join(program_path, 'upload')

  添加下載文件訪問路由,設置後只要放在upload目錄下的文件均可以直接經過瀏覽器下載

@get('/upload/<filepath:path>')
def upload_static(filepath):
    """設置靜態內容路由"""
    response.add_header('Content-Type', 'application/octet-stream')
    return static_file(filepath, root=upload_path)

 

  作完以上設置,上傳與更新就沒有問題了,上傳的圖片直接使用http://127.0.0.1:9090/upload/xxx.jpg方式就能夠訪問了,若是想要使用81端口,也就是經過nginx訪問,那就須要再配置一下

  打開nginx配置文件 :E:\Service\nginx-1.11.5\conf\nginx.conf

  將location ~* ^/(index|api)/ 修改成 location ~* ^/(index|api|upload)/ 

  而後在windows任務管理器(鍵盤同時按Ctrl+Alt+Del鍵,點擊啓動任務管理器),找到nginx_service.exe,右鍵=》結束進程樹

  從新打開服務(控制面板=》全部控制面板項=》管理工具=》服務),啓動nginx_service服務

 

  前端頁面相關修改

  向/lib/ueditor/1.4.3/目錄中添加python文件夾,將添加config.json這個配置項

  修改/lib/ueditor/1.4.3/ueditor.config.js 配置項中 服務器統一請求接口路徑 爲 /api/files/

  本文對應的源碼包裏有ueditor編輯器最新代碼(剛剛去百度下載的),去掉了多餘的文件,你們可直接刪除lib目錄裏的ueditor這個文件夾,使用源碼包裏的替換上去就能夠了

  前端頁面的javascript腳本添加了ueditor編輯器初始化、文件上傳和表單提交等功能,可直接替換about_edit.html文件,具體你們本身研究一下。

  最終效果:

  

    

 

  另外,聯繫咱們的功能與公司介紹差很少,在這裏留一下做業給你們本身嘗試作一個聯繫咱們編輯頁面出來,下一篇會給聯繫咱們編輯頁面源碼給你們

 

  本文對應的源碼下載

 

版權聲明:本文原創發表於 博客園,做者爲 AllEmpty 本文歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然視爲侵權。

python開發QQ羣:669058475(本羣已滿)、733466321(能夠加2羣)    做者博客:http://www.cnblogs.com/EmptyFS/

相關文章
相關標籤/搜索