mini-web框架
1.web框架与web服务器的关系
web服务器主要是接收用户的http请求,根据用户的请求返回不同的资源数据,静态web服务器返回的是静态数据,想要web服务器返回动态资源该怎么处理?
使用web框架专门负责处理用户的动态资源请求,web框架其实就是一个为web服务器提供服务的应用程序,简称web框架
web浏览器《---------http协议------》web服务器《-------.html动态资源请求(WSGI)协议------》web框架应用程《---------------》模板(静态html网页,但没有数据)
web服务器《----------静态资源请求---------------》静态资源
web框架应用程序《------动态数据索引-------------》数据库
-
说明:
- web服务器接收浏览器发起的请求,解析请求报文,若是动态资源请求,找web框架来处理
- web框架负责处理浏览器动态资源请求,把处理的结果发送给web服务器
- web服务器把响应结果封装成响应报文发送浏览器
-
静态资源:
- 不需要经常变化的资源,这种资源web服务器可以提前准备好,如:js/css/jpg
-
动态资源:
- 和静态资源相反,这种资源会经常变化,如:天猫信息,这种资源,web服务器无法提前准备,需要web框架来帮助web服务器进行准备,web服务器可以把.html的资源请求认为是动态资源请求,交由web框架进行处理
-
WSGI协议:
- 是web服务器和web框架之间进行协同工作的一个规定,WSGI协议规定web服务器把动态资源请求的请求信息传给web框架处理,web框架把处理好的结果返回给web服务器。
-
web框架是专门为web服务器处理动态资源请求的一个应用程序
-
web框架和web服务器的关系是web框架专门服务于web服务器,给web服务器提供处理动态资源请求的服务
2.web框架开发-获取处理结果
- web框架职责:接收web服务器的动态资源请求,给web服务器提供处理动态资源请求的服务
- 动态资源判断:
- 根据请求资源路径的后缀名判断:
- 若请求资源路径的后缀名是.html则是动态资源请求,让web框架程序进行处理
- 反之是静态资源请求,让web服务器处理
- 根据请求资源路径的后缀名判断:
#服务器:
#判断是否是动态资源请求,以后把后缀是.html的请求任务认为是动态资源请求
if response_html.endswith('.html'):
'''动态资源请求'''
#动态资源请求找web框架进行处理,需要请求参数给web框架
#准备给web框架的参数信息,都要放到字典里 env协议规定的请求环境
env={
'request_path':response_html,
#传入请求头信息 额外的参数可在字典字典添加
}
#使用框架处理动态的资源请求
# 1.web框架需要把处理结果 返回给web服务器
#2.web服务器负责 将返回的结果 封装成响应报文 发送给浏览器
#状态信息 响应头信息 响应体信息
status,headers,response_body=framework.handle_request(env)
print(status,headers,response_body)
#响应行
response_line='HTTP/1.1 %s\r\n'%status
#响应头
response_header=''
for header in headers:
response_header='%s: %s\r\n'%header
#空行
#响应体
#响应报文
response_data=(response_line+response_header+'\r\n'+response_body).encode('utf-8')
#发送响应报文给浏览器
new_socket.send(response_data)
#关闭
new_socket.close()
else:
'''静态资源请求'''
#判断请求页是否存在 返回静态资源
try:
...
#web框架:
import time
#获取首页数据
def index():
#状态信息
status='200 OK'
#响应头信息 可以是字典/元组
response_header=[("Server","PWS/1.1")]
#1.打开指定的模板文件,读取模板文件中的数据
with open('./wenjian/template/ind.html','r')as f:
f_data=f.read()
#2.查询数据库,模板里面的模板变量{%data%} 替换成从数据库查询到的数据
#响应体 web处理后的数据
#3.模拟查询到的数据
data=time.ctime()#获取当前时间
#4.模板数据替换
response_body=f_data.replace('{%data%}',data)
#返回的是一个元组
return status,response_header,response_body
#处理没有动态资源
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息 可以是字典/元组
response_header = [("Server", "PWS/1.1")]
# 响应体 web处理后的数据
data = 'Not Found'
# 返回的是一个元组
return status, response_header, data
#处理动态资源请求
def handle_request(env):
#获取动态的请求资源路径
request_path=env['request_path']
print('动态资源请求的地址:',request_path)
#判断请求的资源路径,选择指定的函数处理 对应的动态资源请求
if request_path=='/ind.html':
#首页数据
result=index()
#把处理后的结果返回给web服务器,让服务器拼接响应报文时使用
return result
else:
#没有动态资源数据 返回404状态
result=not_found()
return result
3.路由列表开发
如果框架处理页面请求过多,普通分支无法忍受?
什么是路由?
路由是请求的URL到处理函数的映射,也就是说提前把请求的URL和处理函数关联好
路由列表:
这么多的路由如何管理,可以使用一个路由列表进行管理,通过路由列表保存每一个路由
请求路径 | 处理函数 |
---|---|
/log.html | log函数 |
/index.html | index函数 |
/result.html | result函数 |
#web框架
#路由列表 列表里的每一条记录都是一个路由
route_list=[
('/ind.html',index),
('/rest.html',rest)
]
#处理动态资源请求
def handle_request(env):
#获取动态的请求资源路径
request_path=env['request_path']
print('动态资源请求的地址:',request_path)
#遍历路由列表,匹配请求的url
for path,func in route_list:
print(path,func)
if request_path==path:
#找到了指定路由,执行相应的处理函数
result=func()
return result
else:
# 没有动态资源数据 返回404状态
result = not_found()
return result
4.装饰器方式添加路由
通过装饰器的完成路由的自动添加,使用装饰器对处理函数进行装饰的时候,需要知道装饰的函数和请求路径进行相关联,也就是说装饰器需要接受一个url参数,因为定义的装饰器是一个有参数的装饰器
#web框架
import time
#路由列表 列表里的每一条记录都是一个路由
#Django路由列表
route_list=[
# ('/ind.html',index),
# ('/rest.html',rest)
]
#定义一个有参数的装饰器
def route(path):
#装饰器 当执行装饰器的时候就需要将路由添加到路由列表中
def decorator(func):
#装饰函数的时候添加一次即可
route_list.append((path,func))
def inner():
result=func()
return result
return decorator
#Flask使用
@route('/ind.html')
#获取首页数据
def index():
#状态信息
status='200 OK'
#响应头信息 可以是字典/元组
response_header=[("Server","PWS/1.1")]
#1.打开指定的模板文件,读取模板文件中的数据
with open('./wenjian/template/ind.html','r')as f:
f_data=f.read()
#2.查询数据库,模板里面的模板变量{%data%} 替换成从数据库查询到的数据
#响应体 web处理后的数据
#3.模拟查询到的数据
data=time.ctime()#获取当前时间
#4.模板数据替换
response_body=f_data.replace('{%data%}',data)
#返回的是一个元组
return status,response_header,response_body
#rest页面
@route('/rest.html')
#获取首页数据
def rest():
#状态信息
status='200 OK'
#响应头信息 可以是字典/元组
response_header=[("Server","PWS/1.1")]
#1.打开指定的模板文件,读取模板文件中的数据
with open('./wenjian/template/rest.html','r')as f:
f_data=f.read()
#2.查询数据库,模板里面的模板变量{%data%} 替换成从数据库查询到的数据
#响应体 web处理后的数据
#3.模拟查询到的数据
data='你好,这里是rest也数据'#获取当前时间
#4.模板数据替换
response_body=f_data.replace('{%data%}',data)
#返回的是一个元组
return status,response_header,response_body
#处理没有动态资源
def not_found():
# 状态信息
status = '404 Not Found'
# 响应头信息 可以是字典/元组
response_header = [("Server", "PWS/1.1")]
# 响应体 web处理后的数据
data = 'Not Found'
# 返回的是一个元组
return status, response_header, data
#处理动态资源请求
def handle_request(env):
#获取动态的请求资源路径
request_path=env['request_path']
print('动态资源请求的地址:',request_path)
#遍历路由列表,匹配请求的url
for path,func in route_list:
print(path,func)
if request_path==path:
#找到了指定路由,执行相应的处理函数
result=func()
return result
else:
# 没有动态资源数据 返回404状态
result = not_found()
return result
5.数据库连接
#web框架
#2.查询数据库,模板里面的模板变量{%data%} 替换成从数据库查询到的数据
#创建连接对象
conn=pymysql.connect(host="localhost",
port=3306,
user="root",
password="123456",
database="jd",
charset="utf8"
)
#获取游标
cursor=conn.cursor()
#执行sql语句
sql="select * from goods;"
cursor.execute(sql)
#获取查询结果
result=cursor.fetchall()
#关闭游标
cursor.close()
#关闭连接对象
conn.close()
#遍历每一条数据,完成数据的封装
data=""
for i in result:
data+='<h3>%s-%s-%s-%s</h3>'%i
#响应体 web处理后的数据
#3.模拟查询到的数据
# data=time.ctime()#获取当前时间
#4.模板数据替换
response_body=f_data.replace('{%data%}',data)
#返回的是一个元组
return status,response_header,response_body
。。。
6.个人数据接口开发
数据接口返回的是一个json格式数据,可以供他人使用
#个人数据中心接口
@route('/center_data.html')
def center_data():
#从数据库查询数据,将查询的数据转换为json格式
#创键连接对象
conn=pymysql.connect(
host='localhost',
user='root',
password='123456',
database='jd',
charset='utf8'
)
#获取游标
cursor=conn.cursor()
#执行sql
sql="select cart.goodsprice,goods.`name` from cart inner join goods on cart.goodsid=goods.id;"
cursor.execute(sql)
#获取查询结果
result=cursor.fetchall()
# print(result)
#关闭游标
cursor.close()
#关闭连接
conn.close()
#将查询结果转换为json数据格式
#1.将元组转换为列表字典
center_data_list=[{'price':str(row[0]),
'name':row[1]
}for row in result]
#2.将列表转转为json字符串数据 需要导包 json ensure_ascii=False表示在控制台显示中文
json_str=json.dumps(center_data_list,ensure_ascii=False)
# print(json_str)
# 状态信息
status = '200 OK'
# 响应头信息 可以是字典/元组
response_header = [("Server", "PWS/1.1"),
# 指定编码格式。因为没有模板文件,可以通过响应头指定编码格式
("Content-Type","text/html;charset=utf-8")]
return status,response_header,json_str
7.ajax请求数据渲染个人中心页面
<script>
$(document).ready(function(){
// 发送ajax请求,获取个人中心数据
$.get("/center_data.html",function (data){
// ajax成功回调函数
// 获取table标签 css样式选择
var $table=$("table");
// 如果指定了返回数据的解析方法为json,则data就是一个js对象
console.log(data)
for(var i=0;i<data.length;i++){
// 根据下标取每一条数据
var oCenterData=data[i];
var oTr='<tr>'+
'<td>'+oCenterData.price+'</td>'+
'<td>'+oCenterData.Lname+'</td>'+'</tr>'
// 给table标添加一行tr标签
$table.append(oTr)
}
},"json");
});
</script>
。。。
<table>
<tr>
<td>你好,这里是ajax请求</td>
<td>{%data%}</td>
</tr>
</table>
#框架端
#rest页面
@route('/rest.html')
#获取首页数据
def rest():
#状态信息
status='200 OK'
#响应头信息 可以是字典/元组
response_header=[("Server","PWS/1.1")]
#1.打开指定的模板文件,读取模板文件中的数据
with open('./wenjian/template/center_data.html','r',encoding='utf-8')as f:
f_data=f.read()
#2.查询数据库,模板里面的模板变量{%data%} 替换成从数据库查询到的数据
#响应体 web处理后的数据
#3.模拟查询到的数据
data='你好,这里是rest也数据'#获取当前时间
#4.模板数据替换
response_body=f_data.replace('{%data%}','')
# response_body=f_data
#返回的是一个元组
return status,response_header,response_body
#个人数据中心接口
@route('/center_data.html')
def center_data():
#从数据库查询数据,将查询的数据转换为json格式
#创键连接对象
conn=pymysql.connect(
host='localhost',
user='root',
password='123456',
database='jd',
charset='utf8'
)
#获取游标
cursor=conn.cursor()
#执行sql
sql="select cart.goodsprice,goods.`name` from cart inner join goods on cart.goodsid=goods.id;"
cursor.execute(sql)
#获取查询结果
result=cursor.fetchall()
# print(result)
#关闭游标
cursor.close()
#关闭连接
conn.close()
#将查询结果转换为json数据格式
#1.将元组转换为列表字典
center_data_list=[{'price':str(row[0]),
'Lname':row[1]
}for row in result]
#2.将列表转转为json字符串数据 需要导包 json ensure_ascii=False表示在控制台显示中文
json_str=json.dumps(center_data_list,ensure_ascii=False)
# print(json_str)
# 状态信息
status = '200 OK'
# 响应头信息 可以是字典/元组
response_header = [("Server", "PWS/1.1"),
# 指定编码格式。因为没有模板文件,可以通过响应头指定编码格式
("Content-Type","text/html/json;charset=utf-8")]
return status,response_header,json_str
8.logging日志
- logging初识
记录程序运行时产生的日志信息 - 记录程序日志的目的:
- 可以很方便的了解程序的运行情况
- 可以分析用户的操作行为,喜好等信息
- 方便开发人眼检查bug
- logging日志级别
- 日志等级可以分为5个,由低到高:
- DEBUG
- INFO
- WARNING
- ERROR
- CRITICAL
- 日志等级说明:
- DEUG:调试时用
- INFO:程序正常运行时使用
- WARNING:程序未正常运行时使用,但并不是错误,如:用户登录密码错误
- ERRRO:程序出错误时使用,如:IO操作失败
- CRITICAL:特别严重的问题,导致程序不能再继续运行使用,如:磁盘空间为空。一般很少使用
- 默认的是WARNING等级,当在WARNING或WARNING之上等级的才记录日志信息
- 日志等级可以分为5个,由低到高:
- logging日志的使用
- 记录方式有两种:
- 输出到控制台
- 保存到日志文件
- 记录方式有两种:
import logging
logging.debug('这是一个debug级别的日志')
logging.info('这是一个info级别的日志')
logging.warning('这是一个warning级别的日志')
logging.error('这是一个error级别的日志')
logging.critical('这是一个critical级别的日志')
输出:
WARNING:root:这是一个warning级别的日志
ERROR:root:这是一个error级别的日志
CRITICAL:root:这是一个critical级别的日志
- 说明:日志信息只显示大于等于WARNNING级别的日志,这说明默认的日志级别是WARNING
- 日志等级和输出格式
#设置日志等级与格式 过去当前时间 输出文件名 第几行
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s-%(filename)s[lineno:%(lineno)d]')
#日志输出到控制台
""""""
#设置日志等级与格式 过去当前时间 输出文件名 第几行 级别 日志信息
logging.basicConfig(level=logging.DEBUG,format='%(asctime)s-%(filename)s[lineno:%(lineno)d]-%(levelname)s-%(message)s')
#日志输出到控制台
""""""
import logging
logging.debug('这是一个debug级别的日志')
logging.info('这是一个info级别的日志')
logging.warning('这是一个warning级别的日志')# WARNING:root:这是一个warning级别的日志
logging.error('这是一个error级别的日志')# ERROR:root:这是一个error级别的日志
logging.critical('这是一个critical级别的日志')# CRITICAL:root:这是一个critical级别的日志
输出:
2021-09-23 20:45:29,571-test.py[lineno:3415]-DEBUG-这是一个debug级别的日志
2021-09-23 20:45:29,572-test.py[lineno:3416]-INFO-这是一个info级别的日志
2021-09-23 20:45:29,572-test.py[lineno:3417]-WARNING-这是一个warning级别的日志
2021-09-23 20:45:29,572-test.py[lineno:3418]-ERROR-这是一个error级别的日志
2021-09-23 20:45:29,572-test.py[lineno:3419]-CRITICAL-这是一个critical级别的日志
- 此后如竟没有炬火,我便是唯一的光