阿里python前后端开发_类Flask实现前后端交互之代码聊天室-阿里云开发者社区

前言

这两天老是做梦,全根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 = "

"+source+"
"

var mywords = $(child_node);

$("#lefttop").append(mywords);

}

function update_robot(result){

console.log('更新聊天机器人代码执行结果。')

// 创建代码返回结果的节点

child_node = "

"+result+"
"

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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值