第一章:手写简易的web框架,了解Django的原理

手写一个简单的web框架

纯手撸简易版的web框架

import socket


# 括号后面什么都不加默认tcp协议
server = socket.socket()
# 访问地址
server.bind(('127.0.0.1', 8080))
# 链接池
server.listen(5)

while True:
    # 等待访问
    conn, addr = server.accept()
    # 接收访问信息
    data = conn.recv(1024)

    # print(data)  # 请求协议
    """
    b'GET / HTTP/1.1\r\n
    Host: 127.0.0.1:8080\r\n
    Connection: keep-alive\r\n
    Cache-Control: max-age=0\r\n
    sec-ch-ua: "Chromium";v="104", " Not A;Brand";v="99", "Google Chrome";v="104"\r\n
    sec-ch-ua-mobile: ?0\r\n
    sec-ch-ua-platform: "Windows"\r\n
    Upgrade-Insecure-Requests: 1\r\n
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36\r\n
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n
    Sec-Fetch-Site: none\r\n
    Sec-Fetch-Mode: navigate\r\n
    Sec-Fetch-User: ?1\r\n
    Sec-Fetch-Dest: document\r\n
    Accept-Encoding: gzip, deflate, br\r\n
    Accept-Language: zh-CN,zh;q=0.9\r\n\r\n'
    """

    # 把请求从二进制改为字符的形式,以便获取请求内容
    data = data.decode('utf-8')
    # tcp是流式协议,这样写就不用每发一个响应就写一个
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    # 以空格为切割符,把请求切成一个列表,这样用户访问的后缀就是索引为1的字符串。例如:http://127.0.0.1:8080/index,index就是用户访问的后缀
    current_path = data.split(' ')[1]
    # print(current_path)  # '/index'
    # 当用户的后缀为index时,返回一个HTML页面
    if current_path == '/index':
        with open(r'一个页面.html', 'rb') as f:
            conn.send(f.read())
    elif current_path == '/liu':
        conn.send(b'liujieshishazi')
    else:
        conn.send(b'hello web')
    # 关闭链接
    conn.close()

手写框架的不足之处

1、代码重复

2、手动处理请求数据繁琐

3、并发问题

基于wsgiref模块进行接收数据和响应数据

导入wsgiref模块:

from wsgiref.simple_server import make_server

from wsgiref.simple_server import make_server


def run(env, response):
    """
    :param env: 请求相关的所有数据,返回一个字典,wsgiref模块帮你处理好http格式的数据,封装成了字典让你更加方便的操作数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据
    """
    response('200 OK', [])  # 响应首行 响应头
    # 从字典中取出数据,key为PATH_INFO的value是用户传的后缀
    current_path = env.get('PATH_INFO')
    if current_path == '/index':
        return [b'index']
    elif current_path == '/liu':
        return [b'liuhousheng']
    return [b'404']


if __name__ == '__main__':
    """
    实时监听127.0.0.1:8080地址,只要有客户端来了
    都会交给run函数处理(加括号触发run函数的运行)
    """
    server = make_server('127.0.0.1', 8080, run)
    # 启动服务器
    server.serve_forever()


wsgiref模块代码优化与封装

"""
运行文件			  用wsgiref模块写出主干
urls.py				路由与视图函数对应关系
views.py			视图函数(后端业务逻辑)
templates文件夹	  专门用来储存HTML文件
"""
# 按照功能的不同拆分之后,后续功能只需要在urls.py书写对应的关系后再去views.py书写业务逻辑即可
运行文件代码
from wsgiref.simple_server import make_server
# 导入文件urls
from urls import urls
# 导入views
from views import *


def run(env, response):
    """
    :param env: 请求相关的所有数据,返回一个字典,wsgiref模块帮你处理好http格式的数据,封装成了字典让你更加方便的操作数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据
    """
    response('200 OK', [])  # 响应首行 响应头
    # 从字典中取出数据,key为PATH_INFO的value是用户传的后缀
    current_path = env.get('PATH_INFO')
    # 定义一个变量为空
    func = None
    # for循环urls列表得到一个个元组
    for url in urls:
        # 取出元组中索引为0的元素,判断是否有获取到的路由
        if current_path == url[0]:
            # 判断成功把元组中索引为1的元素赋值给func变量
            func = url[1]
            # 判断成功立刻结束for循环
            break
    # 判断func是否为空
    if func:
        # func不为空,加括号运行,并把env传过去,以便再进行取值操作
        res = func(env)
    else:
        # func为空,运行404的函数
        res = error(env)
    # 把返回的值编码成二进制的形式传给浏览器
    return [res.encode('utf-8')]


if __name__ == '__main__':
    """
    实时监听127.0.0.1:8080地址,只要有客户端来了
    都会交给run函数处理(加括号触发run函数的运行)
    """
    server = make_server('127.0.0.1', 8080, run)
    # 启动服务器
    server.serve_forever()

urls文件代码
from views import *

urls = [
    ('/index', index),
    ('/login', login),
    ('/error', error)
]

views文件代码
def index(env):
    with open(r'templates/index.html', 'r', encoding='utf-8') as f:
        return f.read()


def login(env):
    with open(r'templates/login.html', 'r', encoding='utf-8') as f:
        return f.read()


def error(env):
    with open(r'templates/error.html', 'r', encoding='utf-8') as f:
        return f.read()

模板语法之jinja2模块

给前端传一个字典数据

# 写在views文件中
# 并且在urls文件中写入路由对应关系:('/get_dic', get_dic)
# 导入jinja2模块
from jinja2 import Template
def get_dic(env):
    # 创建一个字典
    user_dic = {'username': 'liujie', 'password': 123, 'age': 18, 'hobby': 'read'}
    # 打开文件,读模式
    with open(r'templates/get_dic.html', 'r', encoding='utf-8') as f:
        data = f.read()
        
    # 给get_dic.html传递字典user_dic,用变量名user接收,在前端页面就可以使用user访问字典内容
    tmp = Template(data)
    res = tmp.render(user=user_dic)
    
    # 把HTML文件返回浏览器渲染
    return res

HTML输出字典内容的几种方法

<body>
    <h1>这是一个标签</h1>
    // 固定语法,通过双大括号从后端取值
    {{ user }}
    // jinja2与Python语法接近
    // 从字典通过key取值的三种方法
    <div>用户姓名:{{ user.get('username') }}</div>
    <div>用户密码:{{ user.password }}</div>
    <div>用户年龄:{{ user['age'] }}</div>
</body>

结合mysql数据库给前端提交数据

# 写在views文件中
# 并且在urls文件中写入路由对应关系:('/get_user', get_user)
import pymysql
from jinja2 import Template
def get_user(env):
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='123',
        database='get_user',  # 一定要指定库
        charset='utf8',  # 编码千万不要加-,会报错
        # 是否自动提交
        autocommit=True
    )  # 链接数据库
    # 产生一个游标对象(就是用来帮你执行命令的)
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    # SQL语句
    sql = 'select * from user'
    # 返回值是你当前SQL语句所影响的行数
    affect_rows = cursor.execute(sql)
    # 拿到所有的数据
    data_list = cursor.fetchall()
    with open(r'templates/get_user.html', 'r', encoding='utf-8') as f:
        data = f.read()

    tmp = Template(data)
    res = tmp.render(user_dic=data_list)
    return res

模板语法的for循环

<thead>
    <tr>
        <th>id</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>爱好</th>
    </tr>
</thead>
<tbody>
    {% for user in user_dic %}
    <tr>
        <td>{{ user.id }}</td>
        <td>{{ user.name }}</td>
        <td>{{ user.age }}</td>
        <td>{{ user.hobby }}</td>
    </tr>
    {% endfor %}
</tbody>
posted @   7七柒  阅读(43)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Blazor Hybrid适配到HarmonyOS系统
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· 解决跨域问题的这6种方案,真香!
· 一套基于 Material Design 规范实现的 Blazor 和 Razor 通用组件库
· 数据并发安全校验处理工具类
点击右上角即可分享
微信分享提示