02- web-mini框架添加路由、MySQL(二)
本篇在上篇的基础上为其增设路由功能,同时将上篇中的数据库中数据备份添加进去。
一、装饰器
在之前有介绍过为一个函数不改变源代码不改变原函数的调用方式下为其增设附加功能,需要用到装饰器,而在该上篇的web-mini框架中每当服务器发送动态资源请求过来时,我们需要做if判断,那么我们可不可以省去这繁琐的步骤呢?
1.1 通过闭包来实现装饰器:
# 为函数附加新功能 - 计算运算时长
import time
def timmer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
ret = func(*args,**kwargs)
end_time = time.time()
print("spend time --> %s"%(end_time -start_time))
return ret
return wrapper @timmer # 等价于 ==》test = timmer(test)
def test(num):
time.sleep(1)
print("in the test --> %s"%num )
return num*num +1 ret = test(8)
print(ret)
注1:装饰器在原函数在调用之前就以及开始装饰了。即写上@timmer时test1 = timmer(test)就开始执行。下面我们介绍一种由类实现的装饰器:
1.2 由类实现的装饰器
class Test(object):
"""定义一个装饰器类"""
def __init__(self,func):
self.func = func
def __call__(self,*args,**kwargs):
print("在此为函数附加新功能")
ret = self.func(*args,**kwargs)
return ret @Test # get_str = Test(get_str),相当于将get_str传入创建实例对象
def get_str(num):
print("in the get_str-->%s"%num)
return "hello world!"
get_str() # Test(get_str)() ,必须有call魔法方法,实则运行实例的__call__方法
这种由类实现的装饰器,其效果与由闭包实现的装饰器效果几乎一样,但是由于进行一次装饰,需创建一个实例对象,即每次需要开辟一个内存空间,存放着实例属性、方法以及类的中方法的指针等,比较浪费资源,即 杀鸡用牛刀;
1.3 带参数的装饰器
def set_level(level_num): # 用来接收参数
def set_func(func): # 装饰器函数
def call_func(*args,**kwargs):
if level_num = 1:
print("设置权限1")
elif level_num = 2:
print("设置权限2")
else:
print("你个瓜皮,没有这个权限验证")
ret = func(*args,**kwargs)
return ret
return call_func
return set_func @setlevel(1)
def test1():
print("hello world") @setlevel(2)
def test1():
print("妈的个巴子哟") # setlevel(para) --- test1 = set_level(1)
# 1、首先调用set_level,并且传入参数1, --- test1 = set_level(1)
# 2、启动装饰器set_func,装饰函数,--- test1 = set_func(test1)
带参数的装饰器:
1、其最外层函数set_level,相当于一个容器用来封装,存储装饰器和一些变量等;而真正的装饰器部分实则为set_func装饰器;
2、进行装饰时:@setlevel(1)实则执行两步操作:
①、首先调用set_level,并且传入参数1, --- test1 = set_level(1) ;
②、启动装饰器set_func,装饰函数,--- test1 = set_func(test1);
二、静态、动态、伪静态URL
目前开发的网站其实真正意义上都是动态网站,只是URL上有些区别,一般URL分为静态URL、动态URL、伪静态URL,他们的区别是什么?
静态URL 静态URL类似 域名/news/2012-5-18/110.html 我们一般称为 |
动态URL 动态URL类似 域名/NewsMore.asp?id=5 或者 域名/DaiKuan.php?id=17,带有?号的URL,我们一般称为动态网址,每个URL只是一个逻辑地址,并不是真实物理存在服务器硬盘里的。 |
伪静态URL 伪静态URL类似 域名/course/74.html 这个URL和真静态URL类似。他是通过伪静态规则把动态URL伪装成静态网址。也是逻辑地址,不存在物理地址。 |
三者的优缺点:
1、静态URL:网页打开速度快,SEO最好,但是对于大中型网站而言页面多,修改起来不方便,不便管理;
2、动态URL:由于需要调用框架从数据库中读取数据,故网页打开速度不如静态URL,SEO不如静态URL,但是适合中大型网站,修改页面很方便,因为是逻辑地址,所以占用硬盘空间要比纯静态网站小。
3、伪静态URL:其输入浏览器的形式与静态URL相同,但是确实调用框架读取数据库中的数据来实现的,是相对于前两种的折中方案;URL比较友好,利于记忆;修改页面也十分方便,但是设置麻烦,服务器要支持重写规则,小企业网站或者玩不好的就不要折腾;
二、案例
该实例是在上篇(web-mini框架的基本实现(一))的基础上对其进行修改的,即为其添加路由功能,实现伪静态网页、以及替换成数据库中的数据;
服务端web_server.py
import socket
import re
import multiprocessing
import time
# import dynamic.mini_frame
import sys class WSGIServer(object):
def __init__(self, port, app, static_path):
# 1. 创建套接字
self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 2. 绑定
self.tcp_server_socket.bind(("", port)) # 3. 变为监听套接字
self.tcp_server_socket.listen(128) self.application = app
self.static_path = static_path def service_client(self, new_socket):
"""为这个客户端返回数据""" # 1. 接收浏览器发送过来的请求 ,即http请求
# GET / HTTP/1.1
# .....
request = new_socket.recv(1024).decode("utf-8")
# print(">>>"*50)
# print(request) request_lines = request.splitlines()
print("")
print(">"*20)
print(request_lines) # GET /index.html HTTP/1.1
# get post put del
file_name = ""
ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
if ret:
file_name = ret.group(1)
# print("*"*50, file_name)
if file_name == "/":
file_name = "/index.html" # 2. 返回http格式的数据,给浏览器
# 2.1 如果请求的资源不是以.py结尾,那么就认为是静态资源(html/css/js/png,jpg等)
if not file_name.endswith(".html"):
try:
f = open(self.static_path + file_name, "rb")
except:
response = "HTTP/1.1 404 NOT FOUND\r\n"
response += "\r\n"
response += "------file not found-----"
new_socket.send(response.encode("utf-8"))
else:
html_content = f.read()
f.close()
# 2.1 准备发送给浏览器的数据---header
response = "HTTP/1.1 200 OK\r\n"
response += "\r\n"
# 2.2 准备发送给浏览器的数据---boy
# response += "hahahhah" # 将response header发送给浏览器
new_socket.send(response.encode("utf-8"))
# 将response ic.mini_frame.applicationbody发送给浏览器
new_socket.send(html_content)
else:
# 2.2 如果是以.py结尾,那么就认为是动态资源的请求 env = dict() # 这个字典中存放的是web服务器要传递给 web框架的数据信息
env['PATH_INFO'] = file_name
# {"PATH_INFO": "/index.py"}
# body = dynamic.mini_frame.application(env, self.set_response_header)
body = self.application(env, self.set_response_header) header = "HTTP/1.1 %s\r\n" % self.status for temp in self.headers:
header += "%s:%s\r\n" % (temp[0], temp[1]) header += "\r\n" response = header+body
# 发送response给浏览器
new_socket.send(response.encode("utf-8")) # 关闭套接
new_socket.close() def set_response_header(self, status, headers):
self.status = status
self.headers = [("server", "mini_web v8.8")]
self.headers += headers def run_forever(self):
"""用来完成整体的控制""" while True:
# 4. 等待新客户端的链接
new_socket, client_addr = self.tcp_server_socket.accept() # 5. 为这个客户端服务
p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
p.start() new_socket.close() # 关闭监听套接字
self.tcp_server_socket.close() def main():
"""控制整体,创建一个web 服务器对象,然后调用这个对象的run_forever方法运行"""
if len(sys.argv) == 3:
try:
port = int(sys.argv[1]) #
frame_app_name = sys.argv[2] # mini_frame:application
except Exception as ret:
print("端口输入错误。。。。。ret:",ret)
return
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return # mini_frame:application
ret = re.match(r"([^:]+):(.*)", frame_app_name)
if ret:
frame_name = ret.group(1) # mini_frame
app_name = ret.group(2) # application
else:
print("请按照以下方式运行:")
print("python3 xxxx.py 7890 mini_frame:application")
return with open("./web_server.conf") as f:
conf_info = eval(f.read()) # eval(str)函数很强大,官方解释为:将字符串str当成有效的表达式来求值并返回计算结果 # 此时 conf_info是一个字典里面的数据为:
# {
# "static_path":"./static",
# "dynamic_path":"./dynamic"
# } sys.path.append(conf_info['dynamic_path']) # import frame_name --->找frame_name.py
frame = __import__(frame_name) # 返回值标记这 导入的这个模板
app = getattr(frame, app_name) # 此时app就指向了 dynamic/mini_frame模块中的application这个函数 # print(app) wsgi_server = WSGIServer(port, app, conf_info['static_path'])
wsgi_server.run_forever() if __name__ == "__main__":
main()
web_server.py
基于上篇的web_server.py实现了伪静态URL,即读取的URL中的页面filename是以.html结尾即视为动态资源,则调用框架读取数据库中资源替换模板。相当于欺骗浏览器,输入的是静态URL,确实按照动态URL的方式进行处理的;
web_mini框架 mini_frame.py
import re
from pymysql import connect # 方式二:通过定义装饰器,自动添加文件名和函数名的映射关系,实现路由效果 URL_FUNC_DICT = dict()
# 带参数的装饰器
def route(url):
def get_func(func):
URL_FUNC_DICT[url] = func
def call_func(*args,**kwargs):
ret = func()
return ret
return call_func
return get_func def select_date(sql):
conn = connect(host="localhost",port=3306,database="stock_db",user="root",password= "mysql",charset="utf8")
cs = conn.cursor()
cs.execute(sql)
data_content = cs.fetchall() # 得到的是一个元组,里面有很多个元组
cs.close()
conn.close()
return data_content # 1.index = route("/index.py")即 index = get_func
# 2.index = get_func(index)即index = call_func
@route("/index.html")
def index():
with open("./templates/index.html") as f:
content = f.read() sql = "select * from info;"
my_stock_info = select_date(sql) html_template = """
<tr>
<td>{0}</td>
<td>{1}</td>
<td>{2}</td>
<td>{3}</td>
<td>{4}</td>
<td>{5}</td>
<td>{6}</td>
<td>{7}</td>
<td>
<input type="button" value="添加" id="toAdd" name="toAdd" >
</td>
</tr>
"""
html = ""
for temp in my_stock_info:
html += html_template.format(*temp)
# print("----->>>{}<<<---------".format(html)) content = re.sub(r"\{%content%\}", html, content) return content @route("/center.html")
def center():
with open("./templates/center.html") as f:
content = f.read() sql ="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;" my_stock_info = select_date(sql)
# print(mys)
html =""
html_template = """
<tr>
<td>{0}</td>
<td>{1}</td>
<td>{2}</td>
<td>{3}</td>
<td>{4}</td>
<td>{5}</td>
<td>{6}</td>
<td>
<a type="button" class="btn btn-default btn-xs" href="/update/index.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
</td>
<td>
<input type="button" value="删除" id="toDel" name="toDel" >
</td>
</tr>
"""
for temp in my_stock_info:
html += html_template.format(*temp) content = re.sub(r"\{%content%\}", html, content) return content # 方式一:这种方式通过映射,手动定义字典添加key为文件名:value为对应函数名;
# URL_FUNC_DICT = {
# "/index.py":index,
# "/center.py":center # } print(URL_FUNC_DICT) def application(env, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf-8')]) file_name = env['PATH_INFO']
# file_name = "/index.py" run_func = URL_FUNC_DICT[file_name]
return run_func() # if file_name == "/index.py":
# return index()
# elif file_name == "/center.py":
# return center()
# else:
# return 'Hello World! 我爱你中国....'
mini_frame.py
基于上篇的 mini_frame.py 实现了添加路由功能、及替换成MySQL数据库中的资源的效果;
over~~~其他部分基本与其他相同~~~
02- web-mini框架添加路由、MySQL(二)的更多相关文章
- Web API中的路由(二)——属性路由
一.属性路由的概念 路由让webapi将一个uri匹配到对应的action,Web API 2支持一种新类型的路由:属性路由.顾名思义,属性路由使用属性来定义路由.通过属性路由,我们可以更好地控制We ...
- Web API框架学习——路由(一)
HttpConfiguration(ASP.NET Web API管道的配置是通过HttpConfiguration来完成) : 包括路由注册在内的对整个ASP.NET Web API管道的配置是通过 ...
- [Python之路] 使用装饰器给Web框架添加路由功能(静态、动态、伪静态URL)
一.观察以下代码 以下来自 Python实现简易HTTP服务器与MINI WEB框架(利用WSGI实现服务器与框架解耦) 中的mini_frame最后版本的代码: import time def in ...
- ASP.NET Web API框架揭秘:路由系统的几个核心类型
ASP.NET Web API框架揭秘:路由系统的几个核心类型 虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分 ...
- ASP.NET Web API 框架研究 ASP.NET Web API 路由
ASP.NET Web API 核心框架是一个独立的.抽象的消息处理管道,ASP.NET Web API有自己独立的路由系统,是消息处理管道的组成部分,其与ASP.NET路由系统有类似的设计,都能找到 ...
- 从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建
从零开始搭建框架SSM+Redis+Mysql(二)之MAVEN项目搭建 废话不说,直接撸步骤!!! 1.创建主项目:ncc-parent 选择maven创建项目,注意在创建项目中,packing选择 ...
- ASP.NET Web API 框架研究 Web Host模式路由及将请求转出到消息处理管道
Web Host 模式下的路由本质上还是通过ASP.NET 路由系统来进行路由的,只是通过继承和组合的方式对ASP.NET路由系统的内部的类进行了一些封装,产生自己专用一套类结构,功能逻辑基本都是一样 ...
- 国产 WEB UI 框架 (收费)-- Quick UI,Mini UI
国产 WEB UI 框架 (收费)-- Quick UI,Mini UI : http://www.uileader.com/ http://www.miniui.com/
- express框架+jade+bootstrap+mysql开发用户注册登录项目
完整的项目代码(github):https://github.com/suqinhui/express-demo express是基于Node.js平台的web应用开发框架,用express框架开发w ...
随机推荐
- Linux服务器连接不上的几种解决办法
Linux远程服务器连接不上,或连接超时解决办法:1.测试网络是否通: ping 远程IP 2.如果能ping通则表示与服务器网络连接是正常,接下来测试端口:telnet 远程ip 端口 3.如 ...
- Jmeter之测试计划
一.打开jmeter时会有一个测试计划默认显示,界面如下: 二.测试计划各个配置项说明 1.名称:即整个测试计划的名称,已实际项目命名为好: 2.注释:即添加一些备注信息,以便后期回顾时查看: 3.用 ...
- 查看 redis 请求日志
转: 查看 redis 请求日志 2019-05-29 15:34:41 打卤 阅读数 1980更多 分类专栏: other 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转 ...
- Qt QJson解析json数据
Qt QJson解析json数据 //加载根目录文件 void TeslaManageData::loadRootFolderFiles() { QNetworkAccessManager *mana ...
- [LeetCode] 93. Restore IP Addresses 复原IP地址
Given a string containing only digits, restore it by returning all possible valid IP address combina ...
- PAT(B) 1065 单身狗(Java:17分,C:25分)
题目链接:1065 单身狗 (25 point(s)) 题目描述 "单身狗"是中文对于单身人士的一种爱称.本题请你从上万人的大型派对中找出落单的客人,以便给予特殊关爱. 输入格式 ...
- OpenJDK自动安装脚本 InstallOpenJDK.vbs
Oracle JDK 要收费了,Open JDK没有安装包,只有Zip,写了个安装脚本 InstallOpenJDK.vbs Rem ********************************* ...
- vue中$router与$route的区别
$.router是VueRouter的实例,相当于一个全局的路由器对象.包含很多属性和子对象,例如history对象 $.route表示当前正在跳转的路由对象.可以通过$.route获取到name,p ...
- U盘改造计划之PE、kali、U盘三合一
最强U盘攻略之一 前一段时间朋友买电脑问了我一些问题,我突然发现U盘怎么这么便宜,128G金士顿,140?!!!我16年买的可是240啊.买贵一百块,我好方啊.但是我的U盘,我是不会屈服做一个普通的U ...
- iview 表格随着更改刷新
使用location.reload() 或者是 路由的 this.$router.go(0) 进行刷新的时候,是会出现一阵的空白区域的,因为是整个页面的刷新 ,所以比较缓慢,因此使用了provide/ ...