当前位置: 首页 > 工具软件 > MiniWeb > 使用案例 >

python实现miniweb框架

卫宏硕
2023-12-01

用户通过浏览器发送请求到web服务器,服务器根据请求资源的不同采取不同的返回方式,当浏览器请求的是静态资源,服务器直接返回响应资源,当浏览器请求的是动态资源,服务器需要将请求发送给框架,框架调用模板,将请求数据和模板结合返回给服务器,服务器将模板以响应体的方式发送给浏览器。

浏览器读取响应体,若有图片等静态资源,浏览器继续向服务器发送资源请求,服务器直接返回静态资源,所有资源请求完毕,浏览器将最终页面返回给用户。

实现web服务器

HttpServer.py

import socket
import re
import gevent
import sys
import hframe_flask_stock
from gevent import monkey

monkey.patch_all()


class HttpServer():
    # 1 创建化套接字
    def __init__(self,port=80):
        tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 2 设置地址重用
        tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
        # 3 设置套接字地址,绑定端口
        socket_address = ("", port)
        tcp_server_socket.bind(socket_address)
        # 4 设置监听,最大允许客户端等待连接数为128(套接字由主动变为被动)
        tcp_server_socket.listen(128)
        # 5 将套接字保存为实例属性
        self.tcp_server_socket = tcp_server_socket

    def start(self):
        # 6 等待客户端连接,可以同时接受多个连接
        while True:
            # 7 定义函数,实现服务端信息接受与响应
            new_client_socket,ip_port = self.tcp_server_socket.accept()
            print("新客户端连接")
            g1 = gevent.spawn(self.request_handler,new_client_socket)
            # 因为服务器端不会终止,所以协程一定会执行完成
            # g1.join()

    def request_handler(self,new_client_socket):
        # 8 接受浏览器请求,并判断内容
        recv_data = new_client_socket.recv(1024)
        if not recv_data:
            print("浏览器可能关闭了")
            new_client_socket.close()
            return
        # 解码
        request_text = recv_data.decode()
        print("收到的内容",request_text)
        # 对内容根据\r\n进行切分,目的得到请求行
        request_list = request_text.split('\r\n')
        print("分割后的请求行",request_list)
        ret = re.search(r"\s(.*)\s", request_list[0])
        # 判断报文是否有误
        if not ret:
            print("浏览器请求报文有误")
            new_client_socket.close()
            return

        # 获取资源路径
        path_info = ret.group(1)
        print("得到的路径",path_info)
        if path_info == '/':
            path_info = '/index.html'
        # 请求资源路径
        env = {
            "PATH_INFO": path_info
        }
        # 判断.xxx是静态资源还是动态资源 .html调用框架处理动态资源
        if path_info.endswith(".html"):
            status, headers, body = hframe_flask_stock.app(env)
            # 拼接报文
            response_line = "HTTP/1.1 %s\r\n" % status
            response_head = ""
            for header in headers:
                response_head += "%s: %s\r\n" % header
            response_data = response_line + response_head + "\r\n" + body
            # 编码
            response_data = response_data.encode()
            new_client_socket.send(response_data)
            new_client_socket.close()

        else:
            # 9拼接响应报文
            # 9.1 响应行
            response_line = ""
            # 9.2 响应头
            response_head = "Server:py1.0\r\n"
            # 9.3 空行
            response_blank = "\r\n"
            # 9.4 响应体
            # 打开静态资源读取内容,将读取的内容返回给客户端

            try:
                with open("static"+path_info, "rb") as file:
                    # response_content 为二进制文件
                    response_content = file.read()
            except Exception as e:
                response_line = "HTTP/1.1 404 Not Found\r\n"
                response_content = "Error! %s" % str(e)
                # 对返回内容进行编码
                response_content = response_content.encode()
            else:
                response_line = "HTTP/1.1 200 OK\r\n"
            finally:
                # 保存响应报文内容
                response_data = (response_line + response_head + response_blank).encode() +  response_content
                # 11 发送响应报文给客户端
                new_client_socket.send(response_data)
                # 12 关闭此次连接的套接字
                new_client_socket.close()

def main():
    # 将默认端口设置为80
    # 实例化 HttpServer 对象
    httpserver = HttpServer()
    # 启动服务器 准备接受连接
    httpserver.start()

if __name__ == '__main__':

    main()

仿flask框架

 hframe_flask_stock.py

flask 采用装饰器工厂的方式将路径与函数对应起来,并加入到路由列表中。

用到的数据库是stock_db,使用数据库中的info表和focus表提供数据。

import time
import pymysql

# 路由列表 暂时为空
Env = {}

mysql_connect = pymysql.connect(host='127.0.0.1', port=3306, user='root', db='stock_db', password='mysql',charset='utf8')
cursor = mysql_connect.cursor()

#装饰器工厂
def route(url):
    def wrapper(func):
        Env[url] = func
        def inner():
            pass
    return wrapper


@route("/gettime.html")
def get_time():
    return time.ctime()

@route("/index.html")
def get_index():
    with open("./template/index.html", 'r') as f:
        f_content = f.read()

    sql = "select * from info"
    cursor.execute(sql)
    cursor_content = cursor.fetchall()
    # print(str(cursor_content))

    mysql_content = ""
    for line in cursor_content:
        print(line)
        data_str = """
                <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>
                """ % line
        mysql_content += data_str
    f_content = f_content.replace("{%content%}", mysql_content)
    return f_content

@route("/center.html")
def get_center():
    with open("./template/center.html", 'r') as f:
        f_content = f.read()

    sql = "select info.code,info.short,info.chg,info.turnover,info.price,info.highs,focus.note_info from info inner join focus on info.id = focus.info_id"
    cursor.execute(sql)
    mysql_content = ""
    for line in cursor.fetchall():
        data_str = """
                    <tr>
        	    <td>%s</td>
        	    <td>%s</td>
        	    <td>%s</td>
        	    <td>%s</td>
        	    <td>%s</td>
        	    <td>%s</td>
        	    <td>%s</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>
                """ % line
        mysql_content += data_str
    f_content = f_content.replace("{%content%}", mysql_content)
    return f_content

def app(env):
    # 参数env的含义是 web 服务器给框架用户的请求信息
    print("调用框架获取动态资源")
    # print("Env",Env)
    path_info = env["PATH_INFO"]
    if path_info in Env:
        return "200 ok", [('Server', 'py1.0')], Env[path_info]()
    else:
        # 返回错误信息
        return "404 Not Forund", [('Server','py1.0')], "this is test frame"

# print(get_index())

仿django框架

django框架手动将路径与函数的对应关系加到路由列表中。

import time


def get_time():
    return time.ctime()

def get_index():
    with open("./template/index.html", 'r') as f:
        f_content = f.read()

    mysql_content = "这是数据库数据"
    f_content = f_content.replace("{%content%}", mysql_content)
    return f_content

def get_center():
    with open("./template/center.html", 'r') as f:
        f_content = f.read()

    mysql_content = "这是数据库数据"
    f_content = f_content.replace("{%content%}", mysql_content)
    return f_content

# 路由列表
Env = {
    "/gettime.html": get_time,
    "/index.html":get_index,
    "/center.html": get_center
}

def app(env):
    # 参数env的含义是 web 服务器给框架用户的请求信息
    print("调用框架获取动态资源")
    path_info = env["PATH_INFO"]
    if path_info in Env:
        return "200 ok", [('Server', 'py1.0')], Env[path_info]()
    else:
        # 返回错误信息
        return "404 Not Forund", [('Server','py1.0')], "this is test frame"

通过自己简单实现框架,可以对python服务器及框架有更深一步的理解。

 类似资料: