DIY一个web框架

web框架

Web框架(Web framework)是一种开发框架,用来支持动态网站、网络应用和网络服务的开发。这大多数的web框架提供了一套开发和部署网站的方式,也为web行为提供了一套通用的方法。web框架已经实现了很多功能,开发人员使用框架提供的方法并且完成自己的业务逻辑,就能快速开发web应用了。浏览器和服务器的是基于HTTP协议进行通信的。也可以说web框架就是在以上十几行代码基础张扩展出来的,有很多简单方便使用的方法,大大提高了开发的效率。

wsgiref模块

最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。

如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。

正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。这个接口就是WSGI:Web Server Gateway Interface。而wsgiref模块就是python基于wsgi协议开发的服务模块。

from wsgiref.simple_server import make_server


def application(environ, start_response):
    # 按着http协议解析数据:environ---请求
    # 按着http协议组装数据:start_response---响应
    # print(environ)
    # print(type(environ))  # <class 'dict'>
    start_response("200 OK", [("Content-type", "text/html")])  # 这个格式是固定的,必须是这样(响应状态码, [(响应头), (响应头)...])
    # 得到请求路径
    path = environ.get("PATH_INFO")
    # 对请求路径进行判断
    if path == "/login":
        return [b"<h1>login</h1>"]  # 返回必须是列表类型
    elif path == "/index":
        return [b"<h1>index</h1>"]  # 返回必须是列表类型
    else:
        return [b"<h1>hello world</h1>"]  # 返回必须是列表类型


# 封装socket
httped = make_server("", 8090, application)

# 等待用户连接:conn, addr = sock.accept()
httped.serve_forever()  # 会调用application(environ, start_response)

DIY一个web框架

 models.py

# 在数据库生成用户表,在项目执行前只需运行一次即可,把所有创建数据表的操作都放在此文件即可
import pymysql


conn = pymysql.connect(
    host="127.0.0.1",
    port=3306,
    user="root",
    password="123456",
    db="web",
    autocommit=True
)

cur = conn.cursor()

sql = """
create table user_info(
    id int primary key auto_increnment not null,
    name varchar(32) not null,
    pwd varchar(32) not null
"""

cur.execute(sql)

cur.close()
conn.close()

main.py

from wsgiref.simple_server import make_server
from urls import url_patterns


def application(environ, start_response):
    start_response("200 OK", [("Content-type", "text/html")])
    print(environ.get("PATH_INFO"))
    # 客户端请求路径
    path = environ.get("PATH_INFO")

    # 方案1---直接针对路径做判断,然后写业务逻辑代码
    # if path == "/login":
    #     with open("login.html", "rb") as f:
    #         data = f.read()
    #     return [data]
    # elif path == "/index":
    #     with open("index.html", "rb") as f:
    #         data = f.read()
    #     return [data]

    # 方案2---将业务处理逻辑封装成函数,与路径一一匹配放在元组,然后用一个列表放置所有的元组
    # 这样就不用频繁的使用if判断,而且解耦后,如果想增加一个路径,只需要添加一个对应视图函数即可
    # 把url_patterns(路由分发)列表放在urls.py
    # 把视图函数放在views.py
    # 把所有html文件放在templates文件夹下
    func = None
    # 循环列表,取出路径和对应视图函数地址
    for p, f in url_patterns:
        # 如果浏览器请求路径正确,那么把路径对应的视图函数地址赋值给func
        if path == p:
            func = f
            break
    # 如果func不为空,那么执行func,并且把客户端请求信息作为参数,返回func执行后的结果给客户端,如果为空则返回错误信息
    if func:
        return [func(environ)]
    else:
        return [b"404 error"]


httped = make_server("", 8080, application)

httped.serve_forever()

urls.py

from views import *


url_patterns
= [ ("/login", login), ("/reg", reg), ("/auth", auth), ]

views.py

import pymysql
from urllib.parse import parse_qs


def login(environ):
    with open("templates/login.html", "rb") as f:
        data = f.read()
    return data


def reg(environ):
    with open("templates/reg.html", "rb") as f:
        data = f.read()
    return data


def auth(environ):
    """
    以下操作是wsgiref模块读取form表单提交的数据,比Django复杂,但比我们自己处理起来要简单很多了
    :param environ:
    :return:
    """
    try:
        request_body_size = int(environ.get("CONTENT_LENGTH", 0))
    except (ValueError):
        request_body_size = 0

    request_body = environ['wsgi.input'].read(request_body_size)
    data = parse_qs(request_body)
    user = data.get(b"user")[0].decode("utf8")
    pwd = data.get(b"pwd")[0].decode("utf8")
    print(user, pwd)
    # 操作数据库
    conn = pymysql.connect(
        host="127.0.0.1",
        port=3306,
        user="root",
        password="123456",
        db="web",
    )
    cur = conn.cursor()
    sql = "select * from user_info where name=%s and pwd=%s;"
    cur.execute(sql, (user, pwd))
    # 如果用户名密码正确,那么返回index页面,不正确返回错误信息
    if cur.fetchone():
        f = open("templates/index.html", "rb")
        data = f.read()
        return data
    else:
        return b"user or pwd is wrong"

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录</title>
    <link rel="icon" href="//www.jd.com/favicon.ico">
</head>
<body>
<h3>登录页面</h3>
<!--向auth路径提交form表单-->
<form action="http://127.0.0.1:8080/auth" method="post">
    <p>
        姓名:
        <input type="text" name="user">
    </p>
    <p>
        密码:
        <input type="password" name="pwd">
    </p>
    <p>
        <input type="submit" value="提交">
    </p>
</form>

</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
    <link rel="icon" href="//www.jd.com/favicon.ico">
</head>
<body>
<h1>首页</h1>
<img src="https://img0.baidu.com/it/u=2641803730,2025967347&fm=26&fmt=auto" alt="">
</body>
</html>

总结

"""
web框架功能总结
  1. main.py:启动文件,封装了socket
  2. urls.py:路径与视图函数映射关系---url控制器
  3. views.py:视图函数,固定有一个形式参数environ(所有的请求信息都在这个参数里面,请求路径/请求方式/请求数据...)---视图函数
  4. templates文件夹:html文件---模板
  5. models:在项目启动前,在数据库中创建表结构---与数据库相关
"""
while True: print('studying...')
原文地址:https://www.cnblogs.com/xuewei95/p/15535572.html