WSGI_服务器
# no.1 使用多进程实现浏览器与服务器进行通信;
# no.2 改造web服务器,使其可以处理简易的动态资源请求;
# no.3 创建一个框架,将解析浏览器的请求以及发送响应这部分功能分配给web服务器去执行,而将业务逻辑功能分配给框架去做;
# no.4 发现这种实现方式不具备适用性,即换一个框架,或者换一个服务器,那么二者会不兼容,无法实现交互,此时引入WSGI协议;
# no.5 建立WSGI服务器;
# no.5.1 建立一个空的字典,将解析到的file_name加入字典;
# no.5.2 创建一个方法,定义设置响应头信息,注意:一部分响应头信息自己定义,一部分响应头信息由框架给予;
# no.5.3 调用框架中的application接口函数,并且传入字典及方法的引用,使用一个变量接收application函数的返回值,作为响应体信息;
# no.5.4 设置响应行信息;
# no.5.5 对由框架调用本类中设置响应头的方法所产生的响应头信息进行遍历,因为其是一个列表,里面存储的是多个元祖信息,使用变量接收到;
# 的是一个元祖,对元祖使用索引取出字符串信息;
# no.5.6 对响应行,响应头,响应体信息进行拼接,组成一个新的响应信息并使用变量接收,将信息发送给浏览器
# no.6 至此一个WIGI服务器就搭建好了,其核心在于调用框架接口函数application,以及定义类方法在框架中去调用,在本类中去执行;
# no.7 以上仅仅保证了一个特定的框架可以和该服务器进行通信,当我们需要使用不同的服务器与不同的框架进行通信时,并且其中的各种参数都是不固定的,
# 比如端口,文件路径,框架名称,我们可以在执行程序时临时传入一些特定的信息,那么就实现了不同的服务器与不同的框架进行交互.
# no.7.1 在主函数中,我们对程序外的参数进行捕获,使用sys.agrv这个列表就会存储参数的信息,对其长度进行判断;
# no.7.2 使用特定的程序来执行脚本文件,脚本文件中写着执行主程序的语句 以及给其传递的参数,格式为 python xxx.py port frame_app_name:mini_web;
# no.7.3 首先通过sys.argv[1]来获取port,通过sys.argv[2]来获取框架名以及接口函数名;
# no.7.4 这里要进行异常处理,如果获取信息失败,则需要进行异常处理,声明端口或者框架信息输入错误,并且return使得函数终止;
# no.7.5 如果对列表长度判断与规定长度不一致,则需要声明正确的程序执行格式,并且return;
# no.7.6 对与获取到的框架名和接口函数名,我们需要对其进行正则匹配,分别取出框架名以及函数名.
# no.7.7 通过sys.path.append加入具有框架的包的包名,将这个包加入到系统的默认导包路径中去
# no.7.8 接下来要导包了,但是不能直接使用 import 变量名,而要使用__import__(变量名),返回值为一个框架名,使用变量frame接收;
# no.7.9 通过getattr(框架,函数名)返回一个接口函数,使用变量接收;
# no.7.10 至此,我们获取到了框架,框架中的接口函数,以及端口,将这些参数传入到__init__方法中去.
# no.7.11 至此,整个WSGI服务器的逻辑分析就结束了.
import socket
import sys
import multiprocessing
import re
class WIGI_server():
def __init__(self,port,app,static_path):
self.server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.server_socket.bind(("",port))
self.server_socket.listen(128)
self.port = port
self.application = app
self.static_path = static_path
def server_client(self,new_socket):
request = new_socket.recv(1024).decode()
request_lines = request.splitlines()
print("-"*40)
print(request_lines)
print("-"*40)
file_name = " "
ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
if ret :
file_name = ret.group(1)
if file_name == "/":
file_name = "/index.html"
if not file_name.endswith(".py"):
try:
f = open(self.static_path+file_name,"rb")
except:
response = "HTTP/1.1 404 NOT FOUND\r\n"
response += "\r\n"
response += "------404 NOT FOUND------ "
new_socket.send(response.encode("utf-8"))
else:
response_body = f.read()
f.close()
response_header = "HTTP/1.1 200 OK \r\n"
response_header += "\r\n"
new_socket.send(response_header.encode("utf-8"))
new_socket.send(response_body)
else:
env = dict()
env["PATH_INFO"] = file_name
response_body = self.application(env,self.set_response_header)
response_header = "HTTP/1.1 %s" % self.status
for item in self.header:
response_header += "%s:%s" % (item[0],item[1])
response = response_body+response_header
new_socket.send(response.encode())
new_socket.close()
def set_response_header(self,status,header):
self.status = status
self.header = [("sever","mini_web v8.8")]
self.header += header
def run(self):
while True:
new_socket,client_addr = self.server_socket.accept()
pr1 = multiprocessing.Process(target=self.server_socket,args=(new_socket,))
pr1.start()
new_socket.close()
self.server_socket.close()
def main():
if len(sys.argv) == 3:
try:
port = sys.argv[1]
frame_app_name = sys.argv[2]
except Exception as ret:
print("端口输入错误!")
return
else:
print("请按照以下格式启动程序")
print("python do it again.py 8080 mini_frame:application")
return
ret = re.match(r"([^:])+(.*)",frame_app_name)
if ret:
frame_name = ret.group(1)
app_name = ret.group(2)
else:
print("请按照以下格式启动程序")
print("python do it again.py 8080 mini_frame:application")
return
sys.path.append("./dynamic")
frame = __import__(frame_name)
app = getattr(frame,app_name)
with open("web_server.conf") as f:
conf = eval(f.read())
wisi_server = WIGI_server(port,app,conf[ "static_path"])
wisi_server.run()
if __name__ == '__main__':
main()
# Mini_web框架
```
# no.1 实现一个简易版的web框架,并实现路由功能.
# no.2 使用带有参数的装饰器来实现路由功能,程序执行,向字典中自动加入键值对信息,不用再手动输入,键是我们的资源路径,值是我们的函数引用;
# no.3 使用伪静态地址来改造这个框架:浏览器伪造了一个形式为静态地址的动态地址,服务器接收到这个URL,将它归入处理动态资源的逻辑中去,而框架只需将对应位置的.py修改成.html即可;
# no.4 应用mysql,得到数据库中的相关数据来替换html文件中的指定内容;
# no.5 使用正则表达式匹配浏览器发过来的路径信息,这就实现了一个函数对应于多个动态资源请求,不需要为每一个资源请求都在字典中存储一个键值对,设立一个函数,因为这些资源请求的功能都是一样的;
# no.6 执行动态页面的添加股票关注功能;
# no.6.1 首先需要获取股票代码;
# no.6.2 其次需要连接数据库,并向数据库发送sql语句,确认数据表中是否有这个股票代码存在;
# no.6.3 如果发现这个股票代码并不存在,则认为是非法操作,立即终止程序的执行;
# no.6.4 再对关注的focus表中是否有这支股票进行判断,如果有,则提醒用户已经存在这支股票的关注了,终止程序的执行;
# no.6.5 走到这一步,证明关注表中是没有对这支股票进行关注的,所以我们创建sql语句,使用游标发送,然后返回"添加成功的信息";
# no.7 执行取消股票关注的功能;
# no.7.1 代码逻辑与6基本一致,只不过将sql语句换成从focus数据表中删除这支股票信息,然后返回"删除成功"的信息;
# no.8 进入需要执行修改功能的代码部分,分为两个函数,第一个是展示update页面,并将要修改的note_info和对应的股票代码显示出来,第二个是用户修改后,获取用户的修改信息,并将
# 这个信息保存到对应的数据表中的功能
# no.8.1 展示功能首先要获取这支股票代码,然后打开更新页面的文件,读取其中的内容,与数据库进行连接,执行sql语句,获取note_info信息,得到了
# 股票代码以及对应的备注信息,根据正则,将这两个信息替换到content模板文件中去,然后返回修改后的文件内容即可;
# no.8.2 保存修改功能首先同样要获取股票代码信息,然后还要获取用户修改后的备注信息,由于备注信息是中文,在转换为URL时会执行UR编码,
# 所以我们拿到的是一个经过URL编码的备注信息,将其存入到数据库中显然是不行的,所以我们要对URL编码的信息进行解码,获得修改后的备注信息,
# 接下来,就是使用sql语句,将修改后的备注信息替换原有数据表中的备注信息,最终返回"修改成功";
# no .9 至此,整个代码逻辑就结束了
import re
from pymysql import *
import urllib.parse
PATH_INFO = dict()
def route(url):
def add_dict(func):
PATH_INFO[url] = func
def func_in(*args,**kwargs):
return func(*args,**kwargs)
return func_in
return add_dict
@route("/index.html")
def index(ret):
'''设置主页的一些内容'''
with open("./templates/index.html",encoding = "utf-8") as f:
content = f.read()
coon = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
cs1 = coon.cursor()
cs1.execute("select * from info;")
stock_info = cs1.fetchall()
cs1.close()
coon.close()
html = ""
html_template = """<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
</td>
</tr>
"""
for item in stock_info:
html+=html_template % item
new_content = re.sub(r"/{%content%/}",html,content)
return new_content
@route("/center.html")
def center(ret):
''' 设置个人中心的一些内容'''
with open("./templates/center.html", encoding="utf-8") as f:
content = f.read()
coon = connect(host='localhost', port=3306, user='root', password='mysql', database='stock_db', charset='utf8')
cs2 = coon.cursor()
cs2.execute("select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id;")
stock_info = cs2.fetchall()
cs2.close()
coon.close()
html = ""
html_template = """<tr>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>%s</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="000007">
</td>
</tr>
"""
for item in stock_info:
html += html_template % (item[0],item[1],item[2],item[3],item[4],item[5],item[6])
new_content = re.sub(r"/{%content%/}", html, content)
return new_content
# 执行页面的增加股票备注信息的操作
@route(r"/add/(\d+)\.html") # 向字典添加的键信息是一个正则表达式,分组内的信息是股票代码.
def add_focus(ret):
'''执行将股票代码加入到关注列表的功能'''
# 1.获取股票信息,与数据库连接
stock_code = ret.group(1) # 得到正则表达式中的(\d+)所匹配到的股票代码信息;
conn = connect(host='localhost', port=3306, user='root', password='mysql', database='stock_db', charset='utf8')
cs3 = conn.cursor()
# 2.查询数据库中这个股票代码是否存在,如果不存在,则立即返回.程序结束
sql = "select * from info where code = %s"
cs3.execute(sql,(stock_code,))
result = cs3.fetone()
cs3.close()
conn.close()
if not result: # 如果在已有的数据中没有找到这个股票代码,则认为是非法请求;
return "大哥,请手下留情,我知道你要干什么,我的数据库很脆弱的......"
# 3.判断我们关注的股票信息中是否有这只股票,如果有,就不再重复添加了,立即返回,程序结束
sql = "select * from info as i inner join focus as f on i.id = f.info_id where i.code = %s " # 确定我们关注的表focus中有没有这个股票代码
cs3.execute(sql,(stock_code,))
if cs3.fetchone():
cs3.close()
conn.close()
return "该股票已经被关注,请勿重复添加"
# 4.走到这里,证明数据库中存在这支股票且未被添加关注,下面我们就可以添加这条股票信息;
sql = " insert into focus(info_id) select id from info where code = %s" # 将这只股票代码的ID添加到关注表中去;
cs3.execute(sql,(stock_code,))
conn.commit()
cs3.close()
conn.close()
return "您所添加的股票代码已经关注成功!"
@route(r"/del/(\d+)\.html")
def del_focus(ret):
'''执行取消关注的功能'''
stock_code = ret.group(1)
coon = connect(host='localhost', port=3306, user='root', password='mysql', database='stock_db', charset='utf8')
cs4 = coon.cursor()
sql = "select * from info where code = %s"
cs4.execute(sql,(stock_code,))
# 1.要是没有这只股票,定义为非法请求
if not cs4.fetchone():
cs4.close()
coon.close()
return "大哥,请手下留情,我知道你要干什么,我的数据库很脆弱的......"
# 2.对关注表中有没有这个股票进行判断,如果没有,则返回,并说明并未关注这个股票;
sql = "select * from info as i inner join focus as f on i.id = focus.info_id where i.code = %s "
cs4.execute(sql,(stock_code,))
if not cs4.fetchone():
cs4.close()
coon.close()
return "股票未被关注."
sql = "delete from focus where info_id = (select * from info where code = %s)"
cs4.execute(sql,(stock_code))
coon.commit()
cs4.close()
coon.close()
return "关注股票删除成功"
@route(r"/update/(\d+)\.html")
def show(ret):
'''展示要修改的那个页面'''
stock_info = ret.group(1)
with open("./templates/update.html") as f:
content = f.read()
conn = connect(host='localhost', port=3306, user='root', password='mysql', database='stock_db', charset='utf8')
cs5 = conn.cursor()
sql = "select f.note_info from info as i innner join focus as f on i.id = f.info_id where i.code = %s"
cs5.execute(sql,(stock_info,))
stock_infos = cs5.fetchone()
note_info = stock_infos[0] # 得到这支股票对应的备注信息
cs5.close()
conn.close()
content = re.sub(r"\{%note_info%\}",note_info,content)
content = re.sub(r"\{%code%\}",stock_info,content)
return content
@route(r"/update/(\d+)/(.*)\.html")
def save(ret):
'''保存修改过的备注信息'''
stock_code = ret.group(1)
comment = ret.group(2)
comment = urllib.parse.unquote(comment) # 对浏览器发过来的修改信息进行解码
coon = connect(host='localhost', port=3306, user='root', password='mysql', database='stock_db', charset='utf8')
cs6 =coon.cursor()
sql = "update focus set note_info = %s where info_id = (select id from info where code = %s)"
cs6.execute(sql,(comment,stock_code))
coon.commit()
cs6.close()
coon.close()
return "修改成功"
def application(enviorn,start_response):
start_response("200 OK",[("Content-Type","text/html,charset = utf-8")])
file_name = enviorn["PATH_INFO"]
try:
for url,func in PATH_INFO.items(): # 对字典的键值对组成的元祖组成的列表进行遍历,得到路径信息和函数的引用;
ret = re.match(url,file_name) # 对浏览器传来的动态路径与字典中所存的路径进行匹配;
if ret: # 如果匹配成功;
func() # 则使用函数的引用调用这个函数;
return func(ret) # 返回函数调用的结果
else:
print("请求的动态资源%s不存在!" % file_name)
return
except Exception as ret:
print("异常%s" % str(ret))