web框架推导

软件开发架构

cs架构

bs架构

web框架的本质

所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。

web框架推导

原始自定义web框架

import socket


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)
    conn.send(b'web frame')
    conn.close()

web服务本质就是在上述代码的基础上扩展出来的

在网页上直接输入ip+port(127.0.0.1:8080)得到的结果是127.0.0.1 发送的响应无效。原因是写的服务端向客户端发送的数据格式不符合HTTP协议。

想让客户端(浏览器)收到服务端发送的数据,需要遵循HTTP协议,因此在给客户端回复消息的时候必须按照HTTP协议规则加上响应状态行

低配版自定义web框架 --- 遵循HTTP协议

import socket


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)
    # 响应状态行
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    
    conn.send(b'web frame')
    conn.close()

以上就是web框架的本质,还可以继续进行完善哦

中配版自定义web框架 --- 根据不同的url返回不同的内容

低配版 --- if判断

原理:将浏览器发送到服务端的数据进行处理,然后做一个判断即可

import socket



'''
b'GET /index 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
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\n
Sec-Fetch-Site: cross-site\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'
'''
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)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
    if current_path == '/index':
        conn.send(b'index')
    elif current_path == '/login':
        conn.send(b'login')
    else:
        conn.send(b'web frame')
    conn.close()

中配版 --- 函数版

低配版的问题:虽然解决了不同url路径返回不同内容的需求,但是如果有很多路径要判断怎么办?难道挨个写if判断?

解决办法 --- 使用函数与列表结合

import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

def index(url):
    s = '这是{}'.format(url)
    return bytes(s, encoding='utf-8')

def login(url):
    s = '这是{}'.format(url)
    return bytes(s, encoding='utf-8')

# 定义一个url与函数功能的对应关系的列表
url_list = [
    ('/index',index),
    ('/login',login)
]
while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 定义一个变量名用以保存函数的内存地址
    func = None
    for i in url_list:
        if i[0] == current_path:
            func = i[1]
            break
    if func:
        res = func(current_path)
    else:
        res = b'404 not found'
    conn.send(res)
    conn.close()

中配版 --- 返回HTML文档

import socket


server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

def index(url):
    s = '这是{}'.format(url)
    return bytes(s, encoding='utf-8')

def login(url):
    with open('myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
    return bytes(res,encoding='utf-8')

# 定义一个url与函数功能的对应关系的列表
url_list = [
    ('/index',index),
    ('/login',login)
]
while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 定义一个变量名用以保存函数的内存地址
    func = None
    for i in url_list:
        if i[0] == current_path:
            func = i[1]
            break
    if func:
        res = func(current_path)
    else:
        res = b'404 not found'
    conn.send(res)
    conn.close()

静动态网页

  • 静态网页----网页能够显示,但是页面上的数据是写死的,万年不变

  • 动态网页----数据实时获取,比如后端事件展示到html文件上,数据从数据库获取展示到HTML页面上

案例 --- 前端页面动态展示后端时间

import socket
import datetime

server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)

def index(url):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open('time.time.html','r',encoding='utf-8') as f:

        data = f.read()
        
    # 在网页上定义好特殊符号,用字符串方法替换
    s = data.replace('sfsd',current_time)
    return bytes(s, encoding='utf-8')

def login(url):
    with open('myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
    return bytes(res,encoding='utf-8')

# 定义一个url与函数功能的对应关系的列表
url_list = [
    # (路由,视图函数)
    ('/index',index),
    ('/login',login)
]
while True:
    conn,addr = server.accept()
    data = conn.recv(1024)
    print(data)
    data_list = data.decode('utf-8').split(' ')
    print(data_list)
    current_path = data_list[1]
    conn.send(b'HTTP/1.1 200 OK\r\n\r\n')

    # 定义一个变量名用以保存函数的内存地址
    func = None
    for i in url_list:
        if i[0] == current_path:
            func = i[1]
            break
    if func:
        res = func(current_path)
    else:
        res = b'404 not found'
    conn.send(res)
    conn.close()

wsgiref模块

自定义web框架的不足之处

  • 代码重复,服务端代码所有人都要重新写
  • 手动处理http格式的数据 并且只能拿到url后缀 其他数据获取繁琐(数据格式一样处理的代码其实也大致一样 重复写)
  • 并发的问题

借助wsgiref模块

基础版服务端 --- 根据url返回对应的数据

from wsgiref.simple_server import make_server


def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    if current_path == '/index':
        return [b'index']
    elif current_path == '/login':
        return [b'welcome']
    else:
        return [b'404 error']


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

函数版服务端

from wsgiref.simple_server import make_server
import datetime

# 将env将参数传给视图函数后,视图函数就可以根据浏览器发送给服务端的数据完成各种事件
def index(env):
    with open(r'template/myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
        return res

def longin(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open(r'template/time.time.html','r',encoding='utf-8') as f:
        res = f.read()
    res = res.replace('sfsd',current_time)
    return res

urls = [
    ('/index',index),
    ('/loging',longin)
]


def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    func = None
    for i in url_list:
        if current_path == i[0]:
            func = i[1]
            break
    if func:
        res = func(env)
    else:
        res = '404 not found'
    return [res.encode()]

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

高配版 --- 函数版服务端

上述代码全部放在同一个py文件中是不合理的,后期功能如果增多的话管理/扩展会非常困难

urls.py				路由与视图函数对应关系
views.py			视图函数的逻辑(后端业务逻辑)
templates文件夹	  专门存储html文件
sever.py			服务端文件
  • server.py
from wsgiref.simple_server import make_server
from urls import  urls
from views import *



def run(env,response):
    '''

    :param env: 请求相关的所有数据
    :param response: 响应相关的所有数据
    :return: 返回给浏览器的数据,return [b'']
    '''
    # 响应首行 响应头
    response('200 OK', [])

    # env就是字典格式的HTTP数据
    print(env)
    current_path = env.get('PATH_INFO')
    # wsgiref模块帮你处理号HTTP格式数据,封装成成字典
    func = None
    for i in urls:
        if current_path == i[0]:
            func = i[1]
            break
    if func:
        res = func(env)
    else:
        res = '404 not found'
    return [res.encode()]

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

urls = [
    ('/index',views.index),
    ('/loging',views.longin)
]
  • views.py
import datetime

def index(env):
    with open(r'template/myhtml.html','r',encoding='utf-8') as f:
        res = f.read()
        return res

def longin(env):
    current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
    with open(r'template/time.time.html','r',encoding='utf-8') as f:
        res = f.read()
    res = res.replace('sfsd',current_time)
    return res

模板语法之Jinja2模块

原理

模板的原理就是字符串替换,只要在HTML页面中遵循jinja2的语法规则书写,内部就会按照指定的语法进行替换

模版语法案例一

  • 将字典传递给html文件,并且可以在文件上方便快捷的操作字典数据

(注意要将该函数与路由对应关系加入到urls.py中)

from jinja2 import Template


def get_dict(env):
    user_dic = {'username':'jason','age':18,'hobby':'read'}
    with open(r'templates/04 get_dict.html','r',encoding='utf-8') as f:
        data = f.read()
    tmp = Template(data)
    res = tmp.render(user=user_dic)
    # 给get_dict.html传递了一个值 页面上通过变量名user就能够拿到user_dict,变量名是随意的,不过还是需要见名知意
    return res

# get_dic.html
<body>
{{xxx}} // 获取字典

// 获取字典中的值的三种方式
{{xxx.get('username')}}  
{{xxx['password']}}
{{xxx.hobbies}}
</body>

模板语法案例二 --- 后端数据库中数据展示到前端页面

def get_mysql(env):
    conn = pymysql.connect(
        host='127.0.0.1',
        port=3306,
        user='root',
        password='1026',
        database='user',
        charset='utf8',
        autocommit=True
    )
    cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
    sql = 'select * from info;'
    rows = cursor.execute(sql)
    data_list = cursor.fetchall()
    with open(r'template/get_mysql.html','r',encoding='utf-8') as f:
        data = f.read()
    temp = Template(data)
    res = temp.render(data_list=data_list)
    return res

# get_mysql.html
待定

自定义简易版本web框架请求流程

wsgiref模块
1.请求来的时候解析http格式的数据 封装成大字典
2.响应走的时候给数据打包成符合http格式 再返回给浏览器

posted @ 2020-05-23 11:26  微信搜索-程序媛小庄  阅读(213)  评论(0编辑  收藏  举报