Web框架基础

本章需要掌握

1、http协议:https://www.cnblogs.com/linhaifeng/p/6266327.htm

2、web框架基础:

  egon《web应用》:https://www.cnblogs.com/xiaoyuanqujing/articles/11641028.html

  Jason《web框架前戏》 https://www.cnblogs.com/Dominic-Ji/articles/16294929.html

3、套接字 socket编程:https://www.cnblogs.com/linhaifeng/articles/6129246.html

4、WSGI,wsgiref,werkzeug,uwsgi,uWSGI,nginx

  https://www.cnblogs.com/biggw/p/11342109.html

  https://www.cnblogs.com/quegai18/p/10509996.html

  https://www.cnblogs.com/guokaifeng/p/11007359.html

  https://www.cnblogs.com/wcx666/p/10444400.html

5、Python三大主流框架:https://blog.51cto.com/u_15052541/3051579

下面是web简易框架代码:

app01.py

'''Web应用程序是基于B/S架构的,其中B指的是浏览器,S端由server和application两大部分构成。

我们无需开发浏览器(本质即套接字客户端),只要开发S端即可,S端的本质就是用套接字实现的。''' # S端 # import socket # sock = socket.socket() # sock.bind((ip, port)) # sock.listen(5) # print('Starting development server at http://%s:%s'%(ip,port)) # while True: # conn, addr = sock.accept() # # # 1.接收浏览器发来的请求 # recv_data = conn.recv(1024) # print(recv_data.decode('utf-8')) # # # 2.将请求信息直接转交给application # res = app(recv_data) # # # 3.向浏览器返回消息(此处并没有按照http协议返回) # conn.send(res) # # conn.close() # # def app(environ): # 代表application # # 处理业务逻辑 # return b'hello world' # # if __name__ == '__main__': # make_server('127.0.0.1', 8000, app) """
上面的代码:目前S端已经可以正常接收浏览器发来的请求消息了,但是浏览器在接收到S端回复的响应消息b'hello world'时却无法正常解析 ,
因为浏览器与S端之间收发消息默认使用的应用层协议是HTTP,浏览器默认会按照HTTP协议规定的格式发消息,
而S端也必须按照HTTP协议的格式回消息才行,所以接下来我们详细介绍HTTP协议
套接字工作流程:
服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。
在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。
客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

"""

# S端修订版本:处理HTTP协议的请求消息,并按照HTTP协议的格式回复消息
import socket
def make_server(ip, port, app):   # 创建一个服务器,传入参数,IP,端口port,处理函数app  # ip+port=socket
    sock = socket.socket()   # 绑定协议,生成套接字
    sock.bind((ip, port))   # 绑定自己的IP地址和端口。(ip, port)必须是个元组
    sock.listen(5)   # 开始监听HTTP请求  设置连接最大排队数量
    print('Starting developing server at http://%s:%s/'%(ip, port))  # 服务器启动
    while True:     # 链接循环
        conn, addr = sock.accept()  # 接受链接。对应客户端发过来的操作。客户端返回的是一个小元组,然后解压赋值,把这两个值解压出来。
        # conn是一个对象,封装的就是三次握手的成果(一个双向的通路),addr是客户端的ip和端口。

        # 1.接收并处理浏览器发来的请求
        # 1.1 接收并处理浏览器发来的http协议的消息
        recv_data = conn.recv(1024)   # 最大接收字节数
        # print('recv_data:',recv_data)

        # 1.2 对http协议的消息加以处理,简单示范如下:
        ll = recv_data.decode('utf-8').split('\r\n')
        print('ll:', ll)
        head_ll = ll[0].split(' ')
        print('head_ll:', head_ll)
        environ = {}
        environ['PATH_INFO'] = head_ll[1]
        environ['method'] = head_ll[0]
        print(environ)
        # 打印print(environ)后的结果:
        # {'PATH_INFO': '/', 'method': 'GET'}
        # {'PATH_INFO': '/favicon.ico', 'method': 'GET'}

        # 2. 将请求信息处理后的结果environ交给application,这样application便无需再关注请求信息的处理,可以更加关注于业务逻辑的处理
        res = app(environ)

        # 3. 按照http协议向浏览器返回消息
        # 3.1 返回响应首行
        conn.send(b'HTTP/1.1 200 OK\r\n')
        # 3.2 返回响应头(可以省略)
        conn.send(b'Content-Type: text/html\r\n\r\n')
        # 3.3 返回响应体
        conn.send(res)

        conn.close()    # 结束链接

def app(environ):   # 代表application

    # 处理业务逻辑

    # ①  返回字符串
    # return b'Hello world'

    # ②  返回html页面
    # return b'<h1>hello web</h1><img src="http://www.baidu.com/img/pc_27928619fedf2633952764032537980f.gif"></img>'

    # ③ 打开文件,读取文件内容并返回
    # with open('timer.html', 'r', encoding='utf-8') as f:
    #     data = f.read()
    # return data.encode()

    # ④ 上述S端为浏览器返回的都是静态页面(内容都是固定的),我们还可以返回动态页面(内容是变化的)
    with open('timer.html', 'r', encoding='utf-8') as f:
        data = f.read()

    import time
    now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    data = data.replace('{{ time }}', now)  # 字符串替换
    return data.encode('utf-8')


if __name__ == '__main__':
    make_server('127.0.0.1', 8000, app)  #  在浏览器输入http://127.0.0.1:8000,每次刷新都会看到不同的时间

timer.html文件:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h2>{{ time }}</h2>
</body>
</html>

app02.py

'''
根据app01.py文件中的案例我们可以发现一个规律,在开发S端时,server的功能是复杂且固定的(处理socket消息的收发和http协议的处理),
而app中的业务逻辑却各不相同(不同的软件就应该有不同的业务逻辑),重复开发复杂且固定的server是毫无意义的,有一个wsgiref模块帮我们
写好了server的功能,这样我们便只需要专注于app功能的编写即可
'''

# wsgiref实现了server,即make_server →   def make_server(ip, port, app): 这部分里面的内容
from wsgiref.simple_server import make_server    # wsgiref的simple_server文件封装了make_server函数

def app(environ, start_response): # 代表application
    # 1. 返回http协议的响应首行和响应头信息
    print(environ)
    start_response('200 OK', [('Content-Type', 'text/html')])

    # 2. 处理业务逻辑:根据请求url的不同返回不同的页面内容
    if environ.get('PATH_INFO') == '/index':
        with open('index.html', 'r', encoding='utf-8') as f:
            data = f.read()         #  浏览器输入:http://127.0.0.1:8001/index 显示 主页
    elif environ.get('PATH_INFO') == '/timer':
        with open('timer.html', 'r', encoding='utf-8') as f:
            data = f.read()         #  浏览器输入:http://127.0.0.1:8001/timer 显示 2022-07-21 17:39:51(当前时间)
        import time
        now = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
        data = data.replace('{{ time }}', now)
    else:
        data = '<h1>Hello,web!</h1>'     # 浏览器输入:http://127.0.0.1:8001 显示 Hello,web!

    # 3.返回http响应体信息,必须是bytes类型,必须放在列表中

    return [data.encode('utf-8')]


if __name__ == '__main__':
    # 当接收请求时,wsgiref模块会对该请求加以处理,然后会调用app函数,自动传入两个参数;
    # 1 environ是一个字典,存放了http的请求信息
    # 2 start_response 是一个功能,用于返回http协议的响应首行和和响应头信息
    s = make_server('', 8001, app)  # 代表server
    print('监听8001')
    s.serve_forever() # 表示该服务器在正常情况下将永远运行。  在浏览器输入http://127.0.0.1:8001/index和http://127.0.0.1:8001/timer会看到不同的页面内容

timer.html已经存在了,新增的index.html页面内容如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>主页</h1>
</body>
</html>

app03.py

'''
根据app02.py文件案例中app处理逻辑时根据不同的url地址返回不同的页面,当url地址越来越多,需要写一堆if判断,
代码不够清晰,耦合程度高,所以我们做出以下优化
'''
# 处理业务逻辑的函数
def index(environ):
    with open('index.html', 'r', encoding='utf-8') as f:
        data = f.read()
    return data.encode('utf-8')

def timer(environ):
    import datetime
    now = datetime.datetime.now().strftime('%y-%m-%d %X')       # 浏览器显示:22-07-22 13:22:26 (%y y是小写时,是22,Y大写时是2022; %X 时间的本地版本)
    with open('timer.html', 'r', encoding='utf-8') as f:
        data = f.read()
    data = data.replace('{{ time }}', now)
    return  data.encode('utf-8')

# 路径和函数的映射关系
url_patterns = [
    ('/index', index),
    ('/timer', timer),
]

from wsgiref.simple_server import make_server

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])

    # 拿到请求的url并根据映射关系url_patterns执行响应的函数
    request_url = environ.get('PATH_INFO')
    for url in url_patterns:
        if url[0] == request_url:
            data = url[1](environ)
            break
        else:
            data = b'404'
    return [data]

if __name__ == '__main__':
    s = make_server('',8011, app)
    print('监听8011')
    s.serve_forever()   # 表示该服务器在正常情况下将永远运行。                                                                                                    

 随着业务逻辑复杂度的增加,处理业务逻辑的函数以及url_patterns中的映射关系都会不断地增多,此时仍然把所有代码都放到一个文件中,程序的可读性和可扩展性都会变得非常差,所以我们应该将现有的代码拆分到不同文件中。

关键文件介绍:

-manage.py         项目入口,执行一些命令
-项目名 
    -settings.py   全局配置信息
    -urls.py       总路由,请求地址跟视图函数的映射关系 

-app名字 
    -migrations    数据库迁移的记录 
    -models.py     数据库表模型 
    -views.py      处理业务逻辑的函数,简称视图函数                 

 

代码内容根据egon博客敲的  https://www.cnblogs.com/xiaoyuanqujing/articles/11641028.html

posted @ 2022-07-22 17:22  Tutu007  阅读(75)  评论(0编辑  收藏  举报