类Flask实现前后端交互之代码聊天室

    • 前言
    • 框架
      • 项目目录及各自功能
      • 流程图
    • 后端
      • server
      • backend
      • exector
    • 前端
      • ajax
      • 页面更新
    • 演示
      • 简易应答模式
      • 代理模式处理外部请求
      • 后台日志
    • 总结

前言

这两天老是做梦,全根Python有关,这不昨晚梦见我把Python做成了类似于JavaScript一样的功能,在前端混的风生水起。结果是个梦。。。。。。

在第一次接触了Flask之后,就被它优雅的路由映射给俘获了。后来我自己又搜索了相关的知识点,算是勉强做出一个最最简化的版本。详细的内容可以查看我的这篇文章。
http://blog.csdn.net/marksinoberg/article/details/72811360

关于昨晚的梦,早上醒来倒是给了我一个灵感,为什么不能做出一个代码聊天室呢? 说着可能有点让人摸不着头脑,其实说白了,就是一个本地的代码执行环境。大致的模样应该是这个样子的。

“框架”?

项目目录及各自功能

说到底,这根本不能算是一个框架,充其量也只能是一个工具集吧。项目目录也比较简单。如下:

C:\Users\biao\Desktop\笔记\code-chatter>tree /f .
文件夹 PATH 列表
卷序列号为 E0C6-0F15
C:\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
└─

流程图

大致来说,软件工作的流程如下:

由于作图工具的问题,原本应该双向交互的对象只画出了单个箭头。不过看到这个图后,这个软件的工作流程应该就不难理解了。

后端

基本上来说后端是重中之重啦。接下来一一的介绍一下吧。

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')]

backend

后台存在的意义就是路由映射以及监听客户端请求,并将与请求对应的处理函数进行转发处理。
总的来说类似于一个控制器,或者中间件。用过Flask的童鞋可能会非常容易的理解下面代码的作用了。没用过的话也应该能见名之意。

# coding: utf8
"""
监听客户端请求,返回相应的执行结果。
"""
import os
from server import Application
from executor import runcode
# from jinja2 import Template

app = 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)

通过装饰器就可以实现非常方便的路由映射,结合server的分发处理,就可以实现针对不同的路径实现不同的功能了。

exector

临时代码执行这块稍微有点问题,经过测试,我发现使用subprocess.Popen()并不是一个很好的解决办法。具体表现在:

临时文件清理工作不够及时,不够彻底。

有待进一步改进。

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

# coding: utf8
"""
接受Python脚本,执行相关代码,返回相应结果。
"""

import subprocess
import os

def 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

前端

前端我的思路就是利用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();
    }

页面更新

这里页面更新的触发时机应该是每次点击完发送按钮之后,所以只需要在按钮的响应函数里面添加相应的逻辑即可。

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

演示

下面来看几个图片,聊表心意。

简易“应答”模式

“代理模式”处理外部请求

后台日志


总结

已知问题:

  • make_server 本身的处理问题。
  • temp.py临时文件更新问题
  • 静态文件路径处理的不是很好(⊙﹏⊙)b


完整代码可以到我的GitHub上进行download。

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

时间: 2024-10-14 19:21:51

类Flask实现前后端交互之代码聊天室的相关文章

实现前后端数据交互方法汇总_基础知识

此文章适合前后端协同开发经验不足的新手阅读. HTML赋值 输出到 Element 的 value 或 data-name <input type="hidden" value="<?php echo $user_avatar;?>" /> <div data-value="<?php echo $user_avatar;?>"></div> 渲染结果 <input type=&q

前后端分离之Spring&amp;Vue单页面开发(2)

前言     需求: 最近本人在学习SpringBoot,希望自己能搭一个简单的Demo应用出来,但是搭到前端的时候遇到了困惑,因为网络上大部分教程前端都是应用模板引擎thymeleaf生成的,它给我的感觉就是一个进化版的JSP,但是很明显这种开发方式已经有些落后了.现在前端越来越工程化,Angular/Vue/React等框架非常流行,所以我希望搭建一个更符合技术进步方向的前端应用(我选择了相对容易入门的Vue).     问题: 在查阅资料过程,我发现SpringBoot和Vue相关的入门材

谈前后端分离开发模式

前后端分离的开发模式,原本觉得没什么稀奇的玩艺,在最近参与的一个大型项目中,让我有了更深的理解. 前后端分离的开发模式:系统分析阶段,系分和前端开发人员约定好页面上所需的逻辑变量,进入功能开发阶段,前端开发人员进行前台页面结构,样式,行为层的代码编写,并根据约定好的变量,逻辑规则,完成不同情况展示不同的表现.而后端开发人员,只需要按照约定,赋予这些变量含义,并提供前后端交互所需要的数据即可. 以前自己在php上玩过mvc开发框架,但是没有在这么大型的项目中实践过,所以过程中暴露出一些问题,也说明

前后端分离状态下的工作与交互

为什么要前后端分离 前后端职责不清 在业务逻辑复杂的系统里,我们最怕维护前后端混杂在一起的代码,因为没有约束,M-V-C每一层都可能出现别的层的代码,日积月累,完全没有维护性可言 虽然前后端分离没办法完全解决这种问题,但是可以大大缓解.因为从物理层次上保证了你不可能这么做 开发效率问题 淘宝的Web基本上都是基于MVC框架webx,架构决定了前端只能依赖后端.所以开发模式:前端写好静态demo,后端翻译成VM模版,这种模式的问题就不说了,被吐槽了很久 直接基于后端环境开发也很痛苦,配置安装使用都

我们为什么要尝试前后端分离

这不是一篇纯技术文章,而是一篇分享我个人在前后端分离路上收获的点点滴滴的文章,以此来为准备尝试前后端分离或者想了解前后端分离的童鞋做一个大体的讲解. 尝试与改变 如果你没有尝试过前后端分离的工作流程,那么可以先试想一下这样的流程改变: 把流程从 PM:"我要这个功能" 后端:"这个先找前端做个模板" 前端:"模板做完了" 后端:"我来对接一下,这里样式不对" 前端:"我改完了" 后端:"功能交付&

基于NodeJS的前后端分离的思考与实践(一)全栈式开发_node.js

前言 为了解决传统Web开发模式带来的各种问题,我们进行了许多尝试,但由于前/后端的物理鸿沟,尝试的方案都大同小异.痛定思痛,今天我们重新思考了"前后端"的定义,引入前端同学都熟悉的NodeJS,试图探索一条全新的前后端分离模式. 随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻

DMS前后端技术揭秘及最佳实践

不同于一般的存储和计算产品,云上DMS上属于操作类产品,目的是为用户提供更高更强的数据库访问能力,减少成本以提高效率.本文中,来自阿里巴巴数据库事业部的钟隐分享<DMS前后端技术揭秘及最佳实践>,介绍云上DMS,即数据库管理服务的整体应用和实践. DMS最佳实践 云上DMS从2013年年底上线,从最初仅支持MySQL基本功能,已覆盖了多种RDBMS.NoSQL及部分分析型数据库在内的13种数据源,同时在多种数据库中逐步提供了传统数据库软件所不具有的专业功能,时间有限,我们仅列举4个不同角度的最

云效(原RDC)如何构建一个基于NodeJS的前后端项目

最近在将公司的持续集成架构做一个系统的调整,调整过程中受到了RDC团队大量的帮助,所以利用国庆时间写了几篇RDC的分享,希望能让更多的人了解和用好RDC这个产品. 我会把我最近3个月的使用体会分成5个部分:使用RDC的动机.PHP项目集成.JS项目集成.JAVA项目集成.Docker类项目集成这5个分支来写 因为近期RDC的迭代比较频繁,所以我的分享会比较的浅,点到为止,仅供参考,目录: 1.RDC如何耦合进我们的业务 2.如何构建一个基于Composer的PHP项目 3.如何构建一个基于Nod

当我们准备做前后端分离项目时,我们在考虑什么?

几年前做前后端分离项目的原因,是node刚刚横空出世,业界开始考虑如何真正的用js去写后端服务,于是就借鉴阿里中途岛项目去尝试,主要还是用到了node的密集io场景下的转发. 我们的新项目是采用前后端分离的方式进行开发,这一点主要是基于产品特点考虑而来,产品本身会有很强的富客户端的特点. 我们后端服务面向的客户端包含:iOS,Android,iPad,H5,还有一些游戏场景.所以最好的方式就是后端提供通用的restapi进行数据传输,而前端展示逻辑则交由不同客户端自己实现. 前后端分离项目主要基