1. 程式人生 > >類Flask實現前後端互動之程式碼聊天室

類Flask實現前後端互動之程式碼聊天室

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

                       

前言

這兩天老是做夢,全根Python有關,這不昨晚夢見我把Python做成了類似於JavaScript一樣的功能,在前端混的風生水起。結果是個夢。。。。。。

在第一次接觸了Flask之後,就被它優雅的路由對映給俘獲了。後來我自己又搜尋了相關的知識點,算是勉強做出一個最最簡化的版本。詳細的內容可以檢視我的這篇文章。
http://blog.csdn.net/marksinoberg/article/details/72811360

關於昨晚的夢,早上醒來倒是給了我一個靈感,為什麼不能做出一個程式碼聊天室呢? 說著可能有點讓人摸不著頭腦,其實說白了,就是一個本地的程式碼執行環境。大致的模樣應該是這個樣子的。

程式碼聊天室

“框架”?

專案目錄及各自功能

說到底,這根本不能算是一個框架,充其量也只能是一個工具集吧。專案目錄也比較簡單。如下:

C:\Users\biao\Desktop\筆記\code-chatter>tree /f .資料夾 PATH 列表卷序列號為 E0C6-0
F15C:\USERS\BIAO\DESKTOP\筆記\CODE-CHATTER│  .gitignore│  backend.py              # 服務後臺│  executor.py             # 客戶端程式碼執行工具│  server.py               # 後臺web應用處理器│  temp.py                 # 客戶端臨時程式碼存放│  test.py                 # 測試相關檔案│├─templates│      index.css│      index.html│      index.js│      jquery-2.2.4.min.js└─
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

流程圖

大致來說,軟體工作的流程如下:
軟體工作流程圖

由於作圖工具的問題,原本應該雙向互動的物件只畫出了單個箭頭。不過看到這個圖後,這個軟體的工作流程應該就不難理解了。

後端

基本上來說後端是重中之重啦。接下來一一的介紹一下吧。

server

按照WSGI標準, 一個WEB應用程式或者框架應該滿足如下條件:
 - 本身為一個物件(函式,類init,物件call)
 - 有env, start_response兩個引數(當然名字可以不固定)
 - 返回物件可迭代

我這裡藉助了物件的形式來實現,在__call__方法中添加了處理邏輯。

def __call__(self, env, start_response):        """        根據WSGI標準,web應用程式需要包含兩個引數:        @param env : 一個包含了請求內容的字典        @param start_response : 開始處理來自客戶端的請求        """        path = env["PATH_INFO"]        if path in self.routes:            # 路由對映函式已知            status = '200 OK'            headers = [('Content-Type', 'text/html;charset=UTF-8')]            # print(env)            # 對來自客戶端的請求做封裝處理            request_method = env.get("REQUEST_METHOD", "")            print("***"*28, request_method)            if request_method == "POST":                content_length = int(env.get('CONTENT_LENGTH', 0))                form_data = parse_qs(env.get('wsgi.input', '').read(content_length))                self.request.add(key='method', value="POST")                # TODO 或許在這裡處理一下來自使用者請求的資料,比如escape防止指令碼攻擊                self.request.add(key='post_data', value=form_data)            elif request_method == "GET":                self.request.add(key='method', value=request_method)                query_string = env.get('QUERY_STRING', '')                self.request.add(key='query_string', value=query_string)            start_response(status, headers)            return self.routes[path](self.request)        else:            # 處理函式不包含在路由控制器內            status = '404 Not Found'            headers = [('Content-Type', 'text/html;charset=UTF-8')]            start_response(status, headers)            return ["No handler match for `{}`".format(path).encode('utf8')]
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

backend

後臺存在的意義就是路由對映以及監聽客戶端請求,並將與請求對應的處理函式進行轉發處理。
總的來說類似於一個控制器,或者中介軟體。用過Flask的童鞋可能會非常容易的理解下面程式碼的作用了。沒用過的話也應該能見名之意。

# coding: utf8"""監聽客戶端請求,返回相應的執行結果。"""import osfrom server import Applicationfrom executor import runcode# from jinja2 import Templateapp = Application(__name__)# 下面開始對於前臺的請求做路由控制@app.route('/')def index(request):    """    可以適當的對首頁做下簡介。    """    print("handler方面:", request)    # 也可以嘗試使用模板    with open('./templates/index.html', 'r', encoding="utf8") as f:        html = f.read()        f.close()    yield html.encode('utf8')@app.route('/api/user')def user(request):    print(request.params)    if request.params.get('method', '') == "POST" or request.params.get('method', '')== "GET":        data = request.params.get('post_data')        print(data[b'code'][0].decode('utf8'))        # yield "介面處理相關".encode('utf8')        code = data[b'code'][0].decode('utf8')        result = runcode(code)        yield result    else:        yield "Nothing".encode('utf8')if __name__ == "__main__":    app.run(host='127.0.0.1', port=8888)
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41

通過裝飾器就可以實現非常方便的路由對映,結合server的分發處理,就可以實現針對不同的路徑實現不同的功能了。

exector

臨時程式碼執行這塊稍微有點問題,經過測試,我發現使用subprocess.Popen()並不是一個很好的解決辦法。具體表現在:

 

臨時檔案清理工作不夠及時,不夠徹底。

有待進一步改進。

目前版本也只是夠用。。。。。。

# coding: utf8"""接受Python指令碼,執行相關程式碼,返回相應結果。"""import subprocessimport osdef runcode(data):    """    執行前臺傳過來的Python程式碼    """    # 刪除臨時檔案, 防止上次產生的結果產生影響。    if os.path.exists('temp.py'):        os.remove('temp.py')    with open('temp.py', 'w', encoding='utf8', buffering=1) as f:        f.write(data)        f.close()    # 開啟管道,獲取執行結果    process = subprocess.Popen('python temp.py', stdout=subprocess.PIPE)    data = process.stdout.read()    del process    return data
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

前端

前端我的思路就是利用ajax實現前後端的分離邏輯。讓頁面和資料處理分離開來,更高效的處理各自的事物。

ajax

為了驗證方便性,我用原生的JavaScript和JQuery分別作了實現,發現還是JQuery好用啊,讓我們可以更專注於事物的處理而不是糾結於控制結構上。(⊙﹏⊙)b

function send() {        // 先獲取文字域內的程式碼值        var sourcecode = $("#sourcecode").val();        // var sourcecode = document.getElementById("sourcecode").value;        // 藉助ajax實現功能獲取        var xhr = new XMLHttpRequest();        xhr.onreadystatechange = function () {            if (xhr.readyState == 4) {                console.log(xhr.responseText)            }        }        xhr.open('post', '/api/user')        xhr.send({ 'code': sourcecode })    }    function send2(){        // 更新聊天頁面        update_chat();        // 請求程式碼執行結果        $.ajax({            url: '/api/user',            type: "POST",            dataType: "json",            async: true,            data: {'code': $("#sourcecode").val()},            success: function(response){                console.log("Success.")                console.log(response)                // console.log(response.responseText);                // eval('var data = '+ response.responseText)                // result = response.responseText                update_robot(response.content);            },            error: function(msg){                console.log("Error.")                console.log(msg.responseText);                result = msg.responseText;                update_robot(result);            }        })        // document.getElementById("sourcecode").value = "";        // 更新滾動條,以便於自動上劃。        scroll_top();    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

頁面更新

這裡頁面更新的觸發時機應該是每次點選完傳送按鈕之後,所以只需要在按鈕的響應函式裡面新增相應的邏輯即可。

function update_chat(){        console.log("Ready to append mywords.")        // 先建立本人說話的內容節點        var source = $("#sourcecode").val();        // http://avatar.csdn.net/0/8/F/3_marksinoberg.jpg        child_node = "<div class='mywords'><img src='http://avatar.csdn.net/0/8/F/3_marksinoberg.jpg'><span>"+source+"</span><br /><br /></div>"        var mywords = $(child_node);        $("#lefttop").append(mywords);    }    function update_robot(result){        console.log('更新聊天機器人程式碼執行結果。')        // 建立程式碼返回結果的節點        child_node = "<br /><br /><div class='robot'><span>"+result+"</span><img src='http://avatar.csdn.net/0/B/4/1_yangwei19680827.jpg'></div>"        var robot_words = $(child_node);        $("#lefttop").append(robot_words);    }    // 頁面自動上劃    function scroll_top(){        var messagebox = document.getElementById("lefttop");        messagebox.scrollTop = messagebox.scrollHeight-messagebox.style.height;    }
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

演示

下面來看幾個圖片,聊表心意。

簡易“應答”模式

簡易應答模式

“代理模式”處理外部請求

代理模式處理外部請求

後臺日誌

後臺日誌監測


總結

已知問題:

  • make_server 本身的處理問題。
  • temp.py臨時檔案更新問題
  • 靜態檔案路徑處理的不是很好(⊙﹏⊙)b

完整程式碼可以到我的GitHub上進行download。

https://github.com/guoruibiao/code-chatter

           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述