01-web框架的本质

WEB框架的本质

最基本的需求实现

我们在浏览器中输入一个url,就可以访问到数据。这件事情的本质是什么?

本质上,就是基于socket实现的数据交互,浏览器是socket的客户端,发送一个请求给指定的socket服务端

我们已经学习过了python的网络编程,那我们就利用python实现一个socket的服务端

创建一份叫做manage.py的文件,在其中书写代码

import socket

# 1、买手机
server = socket.socket()
# 2、绑定手机卡
server.bind(('127.0.0.1', 8000))  # 0-65535:0-1024给操作系统使用
# 3、开机
server.listen()
while True:
    # 4、等电话链接
    conn, client_addr = server.accept()
    # 6、收,发消息
    data = conn.recv(1024)  # 1、单位:bytes 2、1024代表最大接收1024个bytes
    print('客户端的数据', data)
    conn.send(b'HELLO')
    # 7、挂电话
    conn.close()
# # 8、关机
server.close()

通过python运行文件的方式将这个程序运行起来

python manage.py

在浏览器的导航栏中输入127.0.0.1:8000

发现出现问题

在这里插入图片描述

出现问题的原因分析

用户的浏览器一输入网址,会给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 你这个网站是这个规定,他那个网站按照他那个规定,这互联网还能玩么?

所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

这个规则就是HTTP协议,以后浏览器发送请求信息也好,服务器回复响应信息也罢,都要按照这个规则来

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含HeaderBody两部分,其中Body是可选的

HTTP请求的格式

在这里插入图片描述

HTTP响应的格式

在这里插入图片描述

浏览器本质上是socket的客户端,会对接无数的socket服务端,所以制定了一系列的规则,如HTTP协议,出现错误的原因是我们的socket服务端返回数据没有遵守HTTP协议的规范,所以浏览器不识别才报错,因此我们需要改写一下我们的代码,让socket服务端遵守HTTP协议的规定,让浏览器能够正常的识别和展示

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8000))  
server.listen()
while True:
    conn, client_addr = server.accept()
    data = conn.recv(1024)  
    print('客户端的数据', data)
    # 服务端响应的数据需要符合HTTP响应格式
    conn.send(b'HTTP1.1 200 OK\r\n\r\n') # 新增一行代码
    conn.send(b'HELLO')
    conn.close()
server.close()

此时,浏览器正常展示内容

在这里插入图片描述

到此为止,我们实现了一个最基本版本的需求

根据不同的URL路径返回不同的内容

最基本的需要实现了,但我们看到的网站都是url路径不同,返回的内容也不一样的,接下来就要实现这个需求

思路:我们可以利用客户端发送过来的数据做文章,从请求相关数据中心拿到请求的url,再做判断

import socket

server = socket.socket()
server.bind(('127.0.0.1', 8000))
server.listen()
while True:
    conn, client_addr = server.accept()
    data = conn.recv(1024)
    data = data.decode().split('\r\n') # 进行切割
    url = data[0].split(' ')[1] # 拿到了url
    conn.send(b'HTTP1.1 200 OK\r\n\r\n')
    # 根据请求的url不同做判断,返回不同的内容
    if url == '/index':
        response = 'index'
    elif url == '/test':
        response = 'test'
    else:
        response = '404 Not Found!'
    # 返回指定的内容
    conn.send(response.encode())
    conn.close()
server.close()

当我们的URL请求越来越多,要写的判断越来越多,我们可以利用函数做优化

import socket

# url对应的请求方法
def index(request):
    return 'index'

def test(request):
    return 'test'

# 定义一个方法  当所有的请求都找不到的时候,就返回错误
def handler404(request):
    return '404 Not found!'

# 定义一个对应关系
urlpatterns = [
    ('/index',index),
    ('/test',test),
]

server = socket.socket()
server.bind(('127.0.0.1', 8000))
server.listen()
while True:
    conn, client_addr = server.accept()
    data = conn.recv(1024)
    data = data.decode().split('\r\n') # 进行切割
    url = data[0].split(' ')[1] # 拿到了url
    conn.send(b'HTTP1.1 200 OK\r\n\r\n')
    # 遍历列表,根据请求的url来执行对应的处理函数
    for line in urlpatterns:
        if url == line[0]:
            response = line[1](data)
            break
    else:
        response = handler404(data)
    # 返回指定的内容
    conn.send(response.encode())
    conn.close()
server.close()

随着url和处理方法越来越多,我们这份文件也变得越来越大,我们可以利用模块对功能进行拆分,放到不同的模块中去,实现解耦合

# manage.py
import socket

from urls import urlpatterns    # 导入urls模块中的urlpatterns

# 定义一个方法  当所有的请求都找不到的时候,就返回错误
def handler404(request):
    return '404 Not found!'

server = socket.socket()
server.bind(('127.0.0.1', 8000))
server.listen()
while True:
    conn, client_addr = server.accept()
    data = conn.recv(1024)
    data = data.decode().split('\r\n') # 进行切割
    url = data[0].split(' ')[1] # 拿到了url
    conn.send(b'HTTP1.1 200 OK\r\n\r\n')
    # 遍历列表,根据请求的url来执行对应的处理函数
    for line in urlpatterns:
        if url == line[0]:
            response = line[1](data)
            break
    else:
        response = handler404(data)
    # 返回指定的内容
    conn.send(response.encode())
    conn.close()
server.close()
# urls.py
import views # 导入views.py

# 定义一个对应关系
urlpatterns = [
    ('/index',views.index),
    ('/test',views.test),
]
# views.py
# url对应的请求方法
def index(request):
    return 'index'

def test(request):
    return 'test'

到目前为止,我们发现,以后我们需要添加更多的请求路径就去urls.py中添加一个对应关系,然后去views.py中添加处理的函数

服务器程序和应用程序

目前我们发现一个问题,我们自己写的socket部分是不完善的,很多问题没解决,如针对HTTP请求数据没有完善的处理方式。

对于真实开发中的web程序来说,一般会分为两部分:服务器程序和应用程序

服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理

应用程序则负责具体的逻辑处理,为了方便应用程序的开发,就出现了众多的Web框架,不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务

这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。

这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。

常用的WSGI服务器有uwsgiGunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref

我们利用wsgiref模块来替换我们自己写的web框架的socket server部分

from wsgiref import simple_server


def run(request, response):
    """
    :param request: 请求相关的数据
    :param response: 响应相关的数据
    :return: 返回给客户端的展示数据
    """
    response('200 OK', [])  # 固定编写 无需掌握    
    return [b'hello']


if __name__ == '__main__':
    server = simple_server.make_server('127.0.0.1', 8080, run)
    '''监听本机8080端口 一旦有请求访问 自动触发run方法的执行'''
    server.serve_forever()
# 模块封装了socket代码并将请求数据处理成诸多k:v键值对
# manage.py
from wsgiref.simple_server import make_server

from urls import urlpatterns


def handler404(request):
    return '404 Not found!'


def run(data,start_response):
    # 设置HTTP响应的状态码和头信息
    start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
    url = data['PATH_INFO']
    for line in urlpatterns:
        if url == line[0]:
            response = line[1](data)
            break
    else:
        response = handler404(data)
    return [response.encode()]


if __name__ == '__main__':
    server = make_server('127.0.0.1',8000,run)
    server.serve_forever()

实现返回HTML页面

返回html页面,本质就是打开一个html文件,读取文件内容并返回

# views.py
def index(request):
    # 为了方便管理,我们将所有的html页面放在一个叫做templates的文件夹里面
    with open('templates/index.html','r',encoding='utf-8') as f:
        data = f.read()
    return data

创建一个叫做templates的文件夹,在其中创建index.html文件即可!

动态渲染数据

但是我们的数据是写死的,怎么才能在html页面中实现动态的数据展示呢

思路: 读取HTML文件就是字符串,利用字符串的替换功能即可实现

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我是index.html页面!</h1>
<h2>现在的随机数是:{{ a }}</h2>
</body>
</html>

# views.py
import random

def index(request):
    with open('templates/index.html','r',encoding='utf-8') as f:
        data = f.read()
    # 定义一个随机数
    num = str(random.randint(1,99))
    # 利用Python字符串的替换,将指定的符号换成我们真实要展示的数据
    return data.replace('{{ a }}',num)

上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2

pip install jinja2
# views.py
import random

from jinja2 import Template

def index(request):
    with open('templates/index.html','r',encoding='utf-8') as f:
        data = f.read()
    template = Template(data)  # 生成模板文件
    str1 = '我是字符串'
    list1 = ['我','爱','你']
    dict1 = {'name':'老杨','age':18}
    res = template.render(locals())  # 渲染数据
    return res

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>我是index.html页面!</h1>
<h2>{{ str1 }}</h2>
<h2>{{ dict1['name'] }}</h2>
<h2>{{ dict1.age }}</h2>
{% for x in list1 %}
    <p>{{ x }}</p>
{% endfor %}
</body>
</html>

总结

至此,我们已经实现了一个简易的web框架,我们只需要在urls.py文件中书写请求路径和处理函数的对应关系,并在views.py中书写处理函数,利用字符串的替换可以实现动态的数据渲染效果

  • manage.py 是我们程序的入口文件,利用这个文件来运行整个程序
  • urls.py是我们的路由文件,里面书写url和处理函数的对应关系
  • views.py是我们的视图文件,里面书写url的处理函数,必须接受一个形参,这个形参里面是请求相关数据

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值