要把js,css都放在static的目录下,因为这些判断为静态资源请求
web服务器处理动态请求:
import socket
import os
import threading
import sys
import framework
import logging
# 在程序入口模块,设置logging日志的配置信息,只配置一次,整个程序都可以使用,好比单例
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s-%(filename)s[lineno:%(lineno)d]-%(levelname)s-%(message)s",
filename="log.txt",
filemode="a")
# http协议的web服务器类
class HttpWebServer(object):
def __init__(self, port):
# 创建tcp服务端套接字
tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口号复用,程序退出端口号立即释放
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
# 绑定端口号
tcp_server_socket.bind(("", port))
# 设置监听
tcp_server_socket.listen(128)
# 把tcp服务器的套接字作为web服务器对象的属性
self.tcp_server_socket = tcp_server_socket
# 处理客户端请求
@staticmethod
def handle_client_request(new_socket):
# 接收客户端的请求信息
recv_data = new_socket.recv(4096)
# 判断接收的数据长度是否为0,如客户连接后,不发请求直接关闭,程序直接return
if len(recv_data) == 0:
new_socket.close()
return
# 对二进制数据进行解码
recv_content = recv_data.decode("utf-8")
print(recv_content)
# 对数据按照空格进行分割,由于客户请求的数据是类似GET /index.html HTTP/1.1\r\nHost: localhost:8000\r\nConnection: max-age=0\r\nUpgrade-Insecure-Request:
#sep=None按照指定字符分割,maxsplit=-1是全部分割,这里是分割2次。
request_list = recv_content.split(" ", maxsplit=2)
# 获取请求的资源路径 --- 如:/index.html
request_path = request_list[1]
print(request_path)
# 判断请求的是否是根目录,如果是根目录设置返回的信息
if request_path == "/":
request_path = "/index.html"
# 判断是否是动态资源请求,以后把后缀是.html的请求任务是动态资源请求
if request_path.endswith(".html"):
"""动态资源请求"""
logging.info("动态资源请求地址:" + request_path)
# 动态资源请求找web框架进行处理,需要把请求参数给web框架
# 准备给web框架的参数信息,都要放到字典里面
#env为环境变量,是字典类型-------------在framework中 handle_request(env), 处理动态资源请求
env = {
"request_path": request_path,
# 传入请求头信息,额外的参数可以在字典里面在进行添加
}
# 使用框架处理动态资源请求,
# 1. web框架需要把处理结果返回给web服务器,
# 2. web服务器负责把返回的结果封装成响应报文发送给浏览器
status, headers, response_body = framework.handle_request(env)
print(status, headers, response_body)
# 响应行 %s占位,将status值传给响应行
response_line = "HTTP/1.1 %s\r\n" % status
# 响应头 %s占位,将header值传给响应行,由于header是元组所以(%s: %s)可直接用%header替换
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:
"""静态资源请求"""
logging.info("静态资源请求地址:" + request_path)
# 1. os.path.exits
# os.path.exists("static/" + request_path)
# 2. try-except
try:
# 打开文件读取文件中的数据, 提示:这里使用rb(二进制)模式,兼容打开图片文件
with open("static" + request_path, "rb") as file: # 这里的file表示打开文件的对象
file_data = file.read()
# 提示: with open 关闭文件这步操作不用程序员来完成,系统帮我们来完成
except Exception as e:
# 代码执行到此,说明没有请求的该文件,返回404状态信息
# 响应行
response_line = "HTTP/1.1 404 Not Found\r\n"
# 响应头
response_header = "Server: PWS/1.0\r\n"
# 读取404页面数据
with open("static/error.html", "rb") as file:
file_data = file.read()
# 响应体
response_body = file_data
# 把数据封装成http 响应报文格式的数据
response = (response_line +
response_header +
"\r\n").encode("utf-8") + response_body
# 发送给浏览器的响应报文数据
new_socket.send(response)
else:
# 代码执行到此,说明文件存在,返回200状态信息
# 响应行
response_line = "HTTP/1.1 200 OK\r\n"
# 响应头
response_header = "Server: PWS/1.0\r\n"
# 响应体
response_body = file_data
# 把数据封装成http 响应报文格式的数据
response = (response_line +
response_header +
"\r\n").encode("utf-8") + response_body
# 发送给浏览器的响应报文数据
new_socket.send(response)
finally:
# 关闭服务于客户端的套接字
new_socket.close()
# 启动服务器的方法
def start(self):
# 循环等待接受客户端的连接请求
while True:
# 等待接受客户端的连接请求
new_socket, ip_port = self.tcp_server_socket.accept()
# 代码执行到此,说明连接建立成功
sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,))
# 设置成为守护主线程
sub_thread.setDaemon(True)
# 启动子线程执行对应的任务
sub_thread.start()
def main():
# 创建web服务器
web_server = HttpWebServer(8000)
# 启动服务器
web_server.start()
# 判断是否是主模块的代码
if __name__ == '__main__':
main()
web框架的职责专门负责处理动态资源请求
"""web框架的职责专门负责处理动态资源请求"""
import time
import pymysql
import json
import logging
# 路由列表, 列表里面的每一条记录都是一个路由
route_list = []
# 定义带有参数的装饰器
def route(path):
# 装饰器
def decorator(func):
# 当执行装饰器的时候就需要把路由添加到路由列表里面,
# 当装饰函数的时候只添加一次路由即可
route_list.append((path, func))
def inner():
result = func()
return result
return inner
# 返回一个装饰器
return decorator
# 获取首页数据
@route("/index.html") # => @decorator => index = decorator(index)
def index():
# 状态信息
status = "200 OK"
# 响应头信息
response_header = [("Server", "PWS/1.1")]
# 1. 打开指定模板文件,读取模板文件中的数据
with open("template/index.html", "r") as file:
file_data = file.read()
# 2. 查询数据库,模板里面的模板变量( {%content%}) 替换成以后从数据库里面查询的数据
# 创建连接对象
conn = pymysql.connect(host="localhost",
port=3306,
user="root",
password="mysql",
database="stock_db",
charset="utf8")
# 获取游标
cursor = conn.cursor()
# 准备sql
sql = "select * from info;"
# 执行sql
cursor.execute(sql)
# 获取查询结果
result = cursor.fetchall()
print(result)
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
# 遍历每一条数据,完成数据的tr标签的封装
data = ""
for row in result:
data += """<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>""" % row
response_body = file_data.replace("{%content%}", data)
# 这里返回的是元组
return status, response_header, response_body
# 个人中心数据接口
@route("/center_data.html")
def center_data():
# 从数据库把数据查询出来,然后把查询处理的数据转成json数据
# 创建连接对象
conn = pymysql.connect(host="localhost",
port=3306,
user="root",
password="mysql",
database="stock_db",
charset="utf8")
# 获取游标
cursor = conn.cursor()
# 准备sql
sql = '''select i.code, i.short, i.chg, i.turnover, i.price, i.highs, f.note_info
from info i inner join focus f
on i.id = f.info_id;
'''
# 执行sql
cursor.execute(sql)
# 获取查询结果
result = cursor.fetchall()
print(result)
# 把元组转成列表字典
center_data_list = [{
"code": row[0],
"short": row[1],
"chg": row[2],
"turnover": row[3],
"price": str(row[4]),
"highs": str(row[5]),
"note_info": row[6]
} for row in result]
print(center_data_list)
# 把列表转成json字符串数据
# ensure_ascii=False 表示在控制台能够显示中文
json_str = json.dumps(center_data_list, ensure_ascii=False)
print(json_str)
print(type(json_str))
# 关闭游标
cursor.close()
# 关闭连接
conn.close()
# 状态信息
status = "200 OK"
# 响应头信息
response_header = [
("Server", "PWS/1.1"),
# 指定编码格式,因为没有模板文件,可以通过响应头指定编码格式
("Content-Type", "text/html;charset=utf-8")
]
return status, response_header, json_str
# 获取个人中心数据
@route("/center.html")
def center():
# 状态信息
status = "200 OK"
# 响应头信息
response_header = [("Server", "PWS/1.1")]
# 1. 打开指定模板文件,读取模板文件中的数据
with open("template/center.html", "r") as file:
file_data = file.read()
# 2. 查询数据库,模板里面的模板变量( {%content%}) 替换成以后从数据库里面查询的数据
response_body = file_data.replace("{%content%}", "")
# 这里返回的是元组
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:
if request_path == path:
# 找到了指定路由,执行对应的处理函数
result = func()
return result
else:
# 没有动态资源数据, 返回404状态信息
result = not_found()
logging.error("没有设置相关的路由信息:" + request_path)
# 把处理后的结果返回给web服务器使用,让web服务器拼接响应报文时使用
return result
前后端分离的写法 center.html为例。http://localhost:8000/center.html,调用时会center.html页面会调用ajax的center_data.html,而center_data.html会调用 framework中的函数。通过pymysql执行数据,得到元组的result, 再 把元组转成列表字典center_data_list = [{ “code”: row[0],“short”: row[1], “chg”: row[2], “turnover”: row[3],“price”: str(row[4]),“highs”: str(row[5]), “note_info”: row[6] } for row in result]
print(center_data_list),然后通过列表转成json字符串数据,返回到页面。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>个人中心 - 个人选股系统 V5.87</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<script src="/js/jquery-1.12.4.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script>
$.get("center_data.html", function (data) {
console.log(data);
},"json");
</script>
<script>
$(document).ready(function(){
// 发送ajax请求,获取个人中心数据
$.get("center_data.html",function (data) {
// ajax 成功回调函数
// 获取table标签
var $table = $(".table");
// 如果指定了返回数据的解析方式是json,那么data就是一个js对象
for(var i = 0; i < data.length; i++){
// 根据下标获取每一个个人中心数据js对象
var oCenterData = data[i];
// 封装后的每一个tr标签
var oTr = '<tr>' +
'<td>'+ oCenterData.code +'</td>' +
'<td>'+ oCenterData.short +'</td>' +
'<td>'+ oCenterData.chg +'</td>' +
'<td>'+ oCenterData.turnover +'</td>' +
'<td>'+ oCenterData.price +'</td>' +
'<td>'+ oCenterData.highs +'</td>' +
'<td>'+ oCenterData.note_info +'</td>' +
'<td><a type="button" class="btn btn-default btn-xs" href="/update/000007.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a></td>' +
'<td><input type="button" value="删除" id="toDel" name="toDel" systemidvaule="000007"></td>' +
'</tr>'
// 给table标签追加每一行tr标签
$table.append(oTr)
}
}, "json");
$("input[name='toDel']").each(function(){
var currentAdd = $(this);
currentAdd.click(function(){
code = $(this).attr("systemIdVaule");
alert("/del/" + code + ".html");
$.get("/del/" + code + ".html", function(data, status){
alert("数据: " + data + "\n状态: " + status);
});
window.location.reload()
});
});
});
</script>
</head>
<body>
<div class="navbar navbar-inverse navbar-static-top ">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" data-toggle="collapse" data-target="#mymenu">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a href="#" class="navbar-brand">选股系统</a>
</div>
<div class="collapse navbar-collapse" id="mymenu">
<ul class="nav navbar-nav">
<li ><a href="/index.html">股票信息</a></li>
<li class="active"><a href="">个人中心</a></li>
</ul>
</div>
</div>
</div>
<div class="container">
<div class="container-fluid">
<table class="table table-hover">
<tr>
<th>股票代码</th>
<th>股票简称</th>
<th>涨跌幅</th>
<th>换手率</th>
<th>最新价(元)</th>
<th>前期高点</th>
<th style="color:red">备注信息</th>
<th>修改备注</th>
<th>del</th>
</tr>
{%content%}
</table>
</div>
</div>
</body>
</html>