[Python]mini-Web框架(一)

简介: [Python]mini-Web框架

image.png


前言

系列文章目录

[Python]目录

视频及资料和课件

链接:https://pan.baidu.com/s/1LCv_qyWslwB-MYw56fjbDg?pwd=1234

提取码:1234

1. web框架概述

1.1 web框架和web服务器的关系介绍

静态web服务器用于静态资源数据请求

动态资源请求使用web框架专门负责处理,这个web框架其实就是一个为web服务器提供服务的应用程序,简称web框架。

关系说明:

  1. web服务器接收浏览器发起的请求,如果是动态资源请求找web框架来处理
  2. web框架负责处理浏览器的动态资源请求,把处理的结果发生给web服务器
  3. web服务器再把响应结果发生给浏览器

1.2 静态资源

不需要经常变化的资源,这种资源web服务器可以提前准备好,比如: png/jpg/css/js等文件。

1.3 动态资源

和静态资源相反, 这种资源会经常变化,比如: 我们在京东浏览商品时经常会根据条件进行筛选,选择不同条件, 浏览的商品就不同,这种资源web服务器无法提前准备好,需要web框架来帮web服务器进行准备,在这里web服务器可以把.html的资源请求认为是动态资源请求交由web框架进行处理。

1.4 WSGI协议

它是web服务器和web框架之间进行协同工作的一个规则,WSGI协议规定web服务器把动态资源的请求信息传给web框架处理,web框架把处理好的结果返回给web服务器。

2. 框架程序开发

2.1 框架职责介绍

接收web服务器的动态资源请求,给web服务器提供处理动态资源请求的服务。

2.2 静态web服务器

import socket
import sys
import threading
# 定义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服务端套接字对象作为属性
        self.tcp_server_socket = tcp_server_socket
    # 启动web服务器进行工作
    def start(self):
        # 循环等待客户端的连接
        while True:
            # 等待接受客户端的连接请求
            new_socket, ip_port = self.tcp_server_socket.accept()
            print(ip_port)
            # 客户端与服务端建立连接后,创建子线程
            sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,))
            # 设置守护主线程
            sub_thread.setDaemon(True)
            # 启动线程
            sub_thread.start()
    # 处理客户端的请求
    @staticmethod
    def handle_client_request(new_socket):
        # 接受客户端发送的数据
        # 由于浏览器一般不会发送大量的数据,所以一次接受的数据大小不用指定很大
        recv_client_data = new_socket.recv(4096)
        # 判断客户端是否处于连接转态
        if len(recv_client_data) == 0:
            new_socket.close()
            return
        # 对二进制数据进行解码
        recv_client_content = recv_client_data.decode('utf-8')
        print(recv_client_content)
        # 对字符串进行分割,分割两次,因为访问的资源路径在第二个位置
        request_list = recv_client_content.split(' ', maxsplit=2)
        # 获取请求资源地址
        request_path = request_list[1]
        print('请求的资源地址为:', request_path)
        # 如果请求的资源地址为 '/', 默认为首页index.html
        if request_path == '/':
            request_path = '/index.html'
        try:
            # 打开读取需要发送给客户端的文件
            with open('static' + request_path, 'rb') as f:
                # 读取文件数据
                file_data = f.read()
        except Exception as e:
            # 打开错误页面
            with open('static/error.html', 'rb') as f:
                # 读取文件数据
                file_data = f.read()
                # 将读取的数据封装为http格式的数据
                # 响应行
                response_line = 'HTTP/1.1 404 Not Found\r\n'
                # 响应头
                response_header = 'Server: PWS1.0\r\n'
                # 响应体
                response_body = file_data
                # 拼接为响应报文
                response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
                # 发送数据给客户端
                new_socket.send(response_data)
        else:
            # 将读取的数据封装为http格式的数据
            # 响应行
            response_line = 'HTTP/1.1 200 OK\r\n'
            # 响应头
            response_header = 'Server: PWS1.0\r\n'
            # 响应体
            response_body = file_data
            # 拼接为响应报文
            response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
            # 发送数据给客户端
            new_socket.send(response_data)
        finally:
            # 关闭与客户端的套接字
            new_socket.close()
# 程序入口函数
def main():
    print('服务器运行在9090端口...')
    # 创建web服务器对象
    # 服务器运行的端口为 9090
    web_server = HttpWebServer(9090)
    # 启动web服务器进行工作
    web_server.start()
# 判断是否为主模块
if __name__ == '__main__':
    main()

2.3 动态资源判断

根据请求资源路径的后缀名进行判断:

(1)如果请求资源路径的后缀名是.html则是动态资源请求, 让web框架程序进行处理。

(2)否则是静态资源请求,让web服务器程序进行处理。

静态web服务器:

# 如果请求的资源地址为 '/', 默认为首页index.html
        if request_path == '/':
            request_path = '/index.html'
        # 判断是动态资源请求还是静态资源请求, 后缀为.html的为动态资源请求
        if request_path.endswith('.html'):
            # 动态资源请求
            # 动态资源请求找web框架处理,需要把请求参数给web框架
            # 准备给web框架的参数信息
            # env 环境变量,WSGI协议规定给web框架的参数信息最好使用env命名
            env = {
                'request_path': request_path
                # 目前只传请求路径
                # 需要传递其他的信息,可以在字典中添加
            }
            # 使用框架处理动态资源请求
            # 1. web框架需要把处理结果返回web服务器
            # 2. web服务器负责把返回的结果封装成响应报文发送给浏览器
            # web框架需要返回 响应状态信息、响应头、响应体
            status, headers, response_body = framework.handle_request(env)
            # 将web框架处理的数据封装成响应报文
            # 响应行
            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:
            # 静态资源请求
            # 执行原先静态web服务器的代码
            # 因为原先写的静态web服务器用于处理静态资源
            try:
                ...
            except Exception as e:
                ...
            else:
                ...
            finally:
                ...
import socket
import sys
import threading
# 导入web框架
import framework
# 定义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服务端套接字对象作为属性
        self.tcp_server_socket = tcp_server_socket
    # 启动web服务器进行工作
    def start(self):
        # 循环等待客户端的连接
        while True:
            # 等待接受客户端的连接请求
            new_socket, ip_port = self.tcp_server_socket.accept()
            print(ip_port)
            # 客户端与服务端建立连接后,创建子线程
            sub_thread = threading.Thread(target=self.handle_client_request, args=(new_socket,))
            # 设置守护主线程
            sub_thread.setDaemon(True)
            # 启动线程
            sub_thread.start()
    # 处理客户端的请求
    @staticmethod
    def handle_client_request(new_socket):
        # 接受客户端发送的数据
        # 由于浏览器一般不会发送大量的数据,所以一次接受的数据大小不用指定很大
        recv_client_data = new_socket.recv(4096)
        # 判断客户端是否处于连接转态
        if len(recv_client_data) == 0:
            new_socket.close()
            return
        # 对二进制数据进行解码
        recv_client_content = recv_client_data.decode('utf-8')
        print(recv_client_content)
        # 对字符串进行分割,分割两次,因为访问的资源路径在第二个位置
        request_list = recv_client_content.split(' ', maxsplit=2)
        # 获取请求资源地址
        request_path = request_list[1]
        print('请求的资源地址为:', request_path)
        # 如果请求的资源地址为 '/', 默认为首页index.html
        if request_path == '/':
            request_path = '/index.html'
        # 判断是动态资源请求还是静态资源请求, 后缀为.html的为动态资源请求
        if request_path.endswith('.html'):
            # 动态资源请求
            # 动态资源请求找web框架处理,需要把请求参数给web框架
            # 准备给web框架的参数信息
            # env 环境变量,WSGI协议规定给web框架的参数信息最好使用env命名
            env = {
                'request_path': request_path
                # 目前只传请求路径
                # 需要传递其他的信息,可以在字典中添加
            }
            # 使用框架处理动态资源请求
            # 1. web框架需要把处理结果返回web服务器
            # 2. web服务器负责把返回的结果封装成响应报文发送给浏览器
            # web框架需要返回 响应状态信息、响应头、响应体
            status, headers, response_body = framework.handle_request(env)
            # 将web框架处理的数据封装成响应报文
            # 响应行
            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:
            # 静态资源请求
            # 执行原先静态web服务器的代码
            # 因为原先写的静态web服务器用于处理静态资源
            try:
                # 打开读取需要发送给客户端的文件
                with open('static' + request_path, 'rb') as f:
                    # 读取文件数据
                    file_data = f.read()
            except Exception as e:
                # 打开错误页面
                with open('static/error.html', 'rb') as f:
                    # 读取文件数据
                    file_data = f.read()
                    # 将读取的数据封装为http格式的数据
                    # 响应行
                    response_line = 'HTTP/1.1 404 Not Found\r\n'
                    # 响应头
                    response_header = 'Server: PWS1.0\r\n'
                    # 响应体
                    response_body = file_data
                    # 拼接为响应报文
                    response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
                    # 发送数据给客户端
                    new_socket.send(response_data)
            else:
                # 将读取的数据封装为http格式的数据
                # 响应行
                response_line = 'HTTP/1.1 200 OK\r\n'
                # 响应头
                response_header = 'Server: PWS1.0\r\n'
                # 响应体
                response_body = file_data
                # 拼接为响应报文
                response_data = (response_line + response_header + '\r\n').encode('utf-8') + response_body
                # 发送数据给客户端
                new_socket.send(response_data)
            finally:
                # 关闭与客户端的套接字
                new_socket.close()
# 程序入口函数
def main():
    print('服务器运行在9090端口...')
    # 创建web服务器对象
    # 服务器运行的端口为 9090
    web_server = HttpWebServer(9090)
    # 启动web服务器进行工作
    web_server.start()
# 判断是否为主模块
if __name__ == '__main__':
    main()

web框架:

"""web框架用于处理动态资源请求"""
import time
def index():
    # 状态信息
    status = '200 OK'
    # 响应头信息
    # 这里使用列表套元组的方法存放信息,每个元组为一个信息
    response_header = [('Server', 'PWS/1.1')]
    # web框架成立后的数据
    # 获取当前时间
    date_now = time.ctime()
    # 返回信息
    # 返回的是元组
    return status, response_header, date_now
def not_found():
    # 状态信息
    status = '404 Not Found'
    # 响应头信息
    # 这里使用列表套元组的方法存放信息,每个元组为一个信息
    response_header = [('Server', 'PWS/1.1')]
    # web框架成立后的数据
    date_now = 'Not Found'
    # 返回信息
    # 返回的是元组
    return status, response_header, date_now
def handle_request(env):
    # 获取动态资源请求路径
    request_path = env['request_path']
    print('动态资源请求路径:', request_path)
    # 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
    if request_path == '/index.html':
        # 返回处理数据
        return index()
    else:
        # 没有动态资源信息,返回404
        return not_found()

3. 模板替换功能开发

模板功能需要实现能够将数据库中查询出来的数据添加到需要返回给浏览器的页面中。

web框架代码:

# 1. 打开指定模板文件,读取模板文件中的数据
    with open('template/index.html', 'r') as f:
        f_data = f.read()
    # 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
    # web框架处理后的数据
    # 获取当前时间,模拟从数据库中查询出来的数据
    date_now = time.ctime()
    response_body = f_data.replace('{%content%}', date_now)
    # 返回信息
    # 返回的是元组
    return status, response_header, response_body
"""web框架用于处理动态资源请求"""
import time
def index():
    # 状态信息
    status = '200 OK'
    # 响应头信息
    # 这里使用列表套元组的方法存放信息,每个元组为一个信息
    response_header = [('Server', 'PWS/1.1')]
    # 1. 打开指定模板文件,读取模板文件中的数据
    with open('template/index.html', 'r') as f:
        f_data = f.read()
    # 2. 查询数据库,模板里面的模板变量({%content%})替换成从数据库中查询出来的数据
    # web框架处理后的数据
    # 获取当前时间,模拟从数据库中查询出来的数据
    date_now = time.ctime()
    response_body = f_data.replace('{%content%}', date_now)
    # 返回信息
    # 返回的是元组
    return status, response_header, response_body
def not_found():
    # 状态信息
    status = '404 Not Found'
    # 响应头信息
    # 这里使用列表套元组的方法存放信息,每个元组为一个信息
    response_header = [('Server', 'PWS/1.1')]
    # web框架成立后的数据
    date_now = 'Not Found'
    # 返回信息
    # 返回的是元组
    return status, response_header, date_now
def handle_request(env):
    # 获取动态资源请求路径
    request_path = env['request_path']
    print('动态资源请求路径:', request_path)
    # 判断请求的动态资源路径,选中指定的函数处理对应的动态资源请求
    if request_path == '/index.html':
        # 返回处理数据
        return index()
    else:
        # 没有动态资源信息,返回404
        return not_found()


相关文章
|
3天前
|
开发框架 数据库 开发者
Web开发新境界:用Python玩转Django和Flask!
【6月更文挑战第12天】Python的Web开发框架Django和Flask各有千秋。Django是全能型框架,适合快速开发大型应用,提供ORM、模板引擎、URL路由和后台管理等全面功能。Flask则轻量级且灵活,适用于小型到中型应用,以其简单易用、高度可扩展和灵活路由著称。两者结合使用,能应对各种Web开发需求。
|
23小时前
|
数据库 Python
Python实践:从零开始构建你的第一个Web应用
使用Python和轻量级Web框架Flask,你可以轻松创建Web应用。先确保安装了Python,然后通过`pip install Flask`安装Flask。在`app.py`中编写基本的"Hello, World!"应用,定义路由`@app.route('/')`并运行`python app.py`启动服务器。扩展应用,可添加新路由显示当前时间,展示Flask处理动态内容的能力。开始你的Web开发之旅吧!【6月更文挑战第13天】
11 2
|
1天前
|
机器人 测试技术 持续交付
Python进行自动化测试测试框架的选择与应用
【6月更文挑战第9天】本文介绍了Python自动化测试的重要性及选择测试框架的考量因素,如功能丰富性、易用性、灵活性和集成性。文中列举了常用的Python测试框架,包括unittest、pytest、nose2和Robot Framework,并提供了使用pytest进行单元测试的示例代码。此外,还展示了如何使用Robot Framework进行验收测试和Web UI测试。选择合适的测试框架对提升测试效率和软件质量至关重要,团队应根据项目需求、社区支持、集成性和学习曲线等因素进行选择。通过不断学习和实践,可以优化自动化测试流程,确保软件的稳定性和可靠性。
7 0
|
2天前
|
XML 数据格式 Python
Python基础教程(第3版)中文版 第15章 python和web(笔记)
Python基础教程(第3版)中文版 第15章 python和web(笔记)
|
3天前
|
前端开发 JavaScript 测试技术
web前端语言框架:探索现代前端开发的核心架构
web前端语言框架:探索现代前端开发的核心架构
15 4
|
4天前
|
分布式计算 负载均衡 并行计算
Python 分布式计算框架 PP (Parallel Python):集群模式下的实践探索
该文介绍了使用Parallel Python (PP) 在两台物理机上构建分布式计算集群的经验。PP是一个轻量级框架,旨在简化Python代码在多处理器系统和集群中的并行执行。文中通过设置子节点的IP、端口和密钥启动PP服务器,并在主节点创建PP实例进行负载均衡。实验使用官方的质数和计算示例,显示PP在集群模式下能有效利用多台机器的多核CPU,实现计算效率的显著提升。未来,作者计划进一步研究PP在更复杂任务和大规模集群中的应用潜力。
|
5天前
|
运维 Serverless API
Serverless 应用引擎产品使用合集之如何实现一键迁移Web框架
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
6天前
|
数据采集 存储 中间件
Scrapy,作为一款强大的Python网络爬虫框架,凭借其高效、灵活、易扩展的特性,深受开发者的喜爱
【6月更文挑战第10天】Scrapy是Python的高效爬虫框架,以其异步处理、多线程及中间件机制提升爬取效率。它提供丰富组件和API,支持灵活的数据抓取、清洗、存储,可扩展到各种数据库。通过自定义组件,Scrapy能适应动态网页和应对反爬策略,同时与数据分析库集成进行复杂分析。但需注意遵守法律法规和道德规范,以合法合规的方式进行爬虫开发。随着技术发展,Scrapy在数据收集领域将持续发挥关键作用。
31 4
|
6天前
|
IDE 测试技术 持续交付
Python作为一种简洁、易读且功能强大的编程语言,其自动化测试和单元测试框架的丰富性和易用性为开发者提供了极大的便利
【6月更文挑战第10天】本文探讨了Python自动化测试与单元测试框架在提升代码质量和效率中的作用。Selenium、Appium和pytest是常用的自动化测试框架,分别支持Web和移动应用的测试。unittest是Python的标准单元测试框架,提供断言方法和测试组织结构。通过制定测试计划、编写高质量测试用例、持续集成与测试、以及有效利用测试报告,开发者能提高代码质量和开发效率。
23 1
|
9天前
|
前端开发 Python