• Web框架本质


    Web框架本质

    我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。

    import socket
    #实例化服务端对象
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #为对象绑定ip地址和端口号
    sk.bind(('127.0.0.1',80))
    #监听端口
    sk.listen(5)
    while 1:
        #获取客户端连接对象和客户端地址信息
        conn,addr = sk.accept()
        #获取客户端发送的信息
        data = conn.recv(9000)
        #响应客户端发送响应行()
        conn.send(b'HTTP/1.1 200 OK
    
    ')
        #发送响应数据
        conn.send(b'<h1>hello kingfan!</h1>')
        #关闭与客户端连接
        conn.close()
    

    可以说Web服务本质上都是在这十几行代码基础上扩展出来的。这段代码就是它们的祖宗。

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

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

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

    HTTP协议主要规定了客户端和服务器之间的通信格式,那HTTP协议是怎么规定消息格式的呢?

    让我们首先打印下我们在服务端接收到的消息是什么。

    import socket
    #实例化服务端对象
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #为对象绑定ip地址和端口号
    sk.bind(('127.0.0.1',80))
    #监听端口
    sk.listen(5)
    while 1:
        #获取客户端连接对象和客户端地址信息
        conn,addr = sk.accept()
        #获取客户端发送的信息
        data = conn.recv(9000)
        print(data)
        #响应客户端发送响应行()
        conn.send(b'HTTP/1.1 200 OK
    
    ')
        #发送响应数据
        conn.send(b'<h1>hello kingfan!</h1>')
        #关闭与客户端连接
        conn.close()
    
    b'GET / HTTP/1.1
    Host: 127.0.0.1:8080
    Connection: keep-alive
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    DNT: 1
    Accept-Encoding: gzip, deflate, br
    Accept-Language: zh-CN,zh;q=0.9
    Cookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv
    
    '
    

    基础自定义web框架

    经过上面的补充学习,我们知道了要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的Web框架了

    import socket
    #实例化服务端对象
    sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #为对象绑定ip地址和端口号
    sk.bind(('127.0.0.1',80))
    #监听端口
    sk.listen(5)
    while 1:
        #获取客户端连接对象和客户端地址信息
        conn,addr = sk.accept()
        #获取客户端发送的信息
        data = conn.recv(9000)
        print(data)
        #响应客户端发送响应行()
        conn.send(b'HTTP/1.1 200 OK
    
    ')
        #发送响应数据
        conn.send(b'<h1>hello kingfan!</h1>')
        #关闭与客户端连接
        conn.close()
    

    我们通过十几行代码简单地演示了web 框架的本质。

    接下来就让我们继续完善我们的自定义web框架吧

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

    这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?

    小事一桩,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...

    import socket
    
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen(5)
    
    
    def login():
        return bytes('欢迎来到登录页面', encoding='utf8')
    
    
    def register():
        return bytes('欢迎来到注册界面', encoding='utf8')
    
    
    def home():
        return bytes('欢迎来到主页面', encoding='utf8')
    
    
    # 返回一个html文件
    def web():
        with open('login.html', 'rb') as f1:
            return f1.read()
    
    
    url_lis = [('/login/', login),
               ('/register/', register),
               ('/home/', home),
               ('/web/', web)
               ]
    while 1:
        conn, addr = sk.accept()
        # 接受请求消息
        data = conn.recv(9000)
        data = data.decode('utf-8')
        # 获取url是什么
        url = (data.split('
    ')[0].split(' ')[1])
        # 在本地url列表中查找url地址有则执行相应的函数
        for i in url_lis:
            if url == i[0]:
                fun = i[1]
                msg = fun()
                break
        # 如果没有则发送错误信息
        else:
            msg = b'404 not found'
        print(msg)
        # 发送响应状态(Content-Type: text/html; charset=utf-8)响应头指定返回数据类型和编码格式
        conn.send(b'HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    
    ')
        # 发送响应数据
        conn.send(msg)
        conn.close()
    

    让网页动态起来

    这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。

    没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)

    import socket, time
    
    sk = socket.socket()
    sk.bind(('127.0.0.1', 8080))
    sk.listen(5)
    
    
    def login():
        return bytes('欢迎来到登录页面', encoding='utf8')
    
    
    def register():
        return bytes('欢迎来到注册界面', encoding='utf8')
    
    
    def home():
        return bytes('欢迎来到主页面', encoding='utf8')
    
    
    def nowtime():
        # 将time.html文件中的XX-XX-XX替换成现在时间
        with open('time.html', 'r', encoding='utf-8') as f1:
            msg = f1.read()
            msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
            return bytes(msg, encoding='utf-8')
    
    
    # 返回一个html文件
    def web():
        with open('login.html', 'rb') as f1:
            return f1.read()
    
    
    url_lis = [('/login/', login),
               ('/register/', register),
               ('/home/', home),
               ('/web/', web),
               ('/time/', nowtime)
               ]
    while 1:
        conn, addr = sk.accept()
        # 接受请求消息
        data = conn.recv(9000)
        data = data.decode('utf-8')
        # 获取url是什么
        url = (data.split('
    ')[0].split(' ')[1])
        # 在本地url列表中查找url地址有则执行相应的函数
        for i in url_lis:
            if url == i[0]:
                fun = i[1]
                msg = fun()
                break
        # 如果没有则发送错误信息
        else:
            msg = b'404 not found'
        print(msg)
        # 发送响应状态(Content-Type: text/html; charset=utf-8)响应头指定返回数据类型和编码格式
        conn.send(b'HTTP/1.1 200 OK
    Content-Type: text/html; charset=utf-8
    
    ')
        # 发送响应数据
        conn.send(msg)
        conn.close()
    
    

    服务器程序和应用程序

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

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

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

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

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

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

    常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。

    从这继续...

    wsgiref

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

    import time
    from wsgiref.simple_server import make_server
    
    
    def login():
        return bytes('欢迎来到登录页面', encoding='utf8')
    
    
    def register():
        return bytes('欢迎来到注册界面', encoding='utf8')
    
    
    def home():
        return bytes('欢迎来到主页面', encoding='utf8')
    
    
    def nowtime():
        # 将time.html文件中的XX-XX-XX替换成现在时间
        with open('time.html', 'r', encoding='utf-8') as f1:
            msg = f1.read()
            msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
            return bytes(msg, encoding='utf-8')
    
    
    # 返回一个html文件
    def web():
        with open('login.html', 'rb') as f1:
            return f1.read()
    
    
    url_lis = [('/login/', login),
               ('/register/', register),
               ('/home/', home),
               ('/web/', web),
               ('/time/', nowtime)
               ]
    
    
    def run(environ, start_response):
        # 设置HTTP响应的状态ma码和头信息(指定返回类型和编码格式)
        start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
        # 获取用户访问的url 本质返回跟前例子一样如/time/
        url = environ['PATH_INFO']
        for i in url_lis:
            if url == i[0]:
                fun = i[1]
                msg = fun()
                break
        # 如果没有则发送错误信息
        else:
            msg = b'404 not found'
        # wsgire模块将msg返回给客户,底层依旧是socket
        return [msg, ]
    
    
    if __name__ == '__main__':
        httpd = make_server('127.0.0.1', 8080, run)
        print('等待连接')
        httpd.serve_forever()
    

    jinja2

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

    import time,pymysql
    from wsgiref.simple_server import make_server
    from jinja2 import Template
    
    def login():
        return bytes('欢迎来到登录页面', encoding='utf8')
    
    
    def register():
        return bytes('欢迎来到注册界面', encoding='utf8')
    
    
    def home():
        return bytes('欢迎来到主页面', encoding='utf8')
    
    
    def nowtime():
        # 将time.html文件中的XX-XX-XX替换成现在时间
        with open('time.html', 'r', encoding='utf-8') as f1:
            msg = f1.read()
            msg = msg.replace('XX-XX-XX', str(time.strftime("%c")))
            return bytes(msg, encoding='utf-8')
    
    def userinfo():
        conn = pymysql.connect(
            host = '127.0.0.1',
            port =3306,
            user = 'root',
            password='1234',
            database='db1',
            charset = 'utf8'
        )
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute('select * from staff_info;')
        user_list1 = cursor.fetchall()
        print(user_list1)
        with open('userinfo.html', 'r', encoding='utf8') as f1:
            data = f1.read()
            template = Template(data)
            msg = template.render({'user_list':user_list1})
            return bytes(msg, encoding="utf8")
    # 返回一个html文件
    def web():
        with open('login.html', 'rb') as f1:
            return f1.read()
    
    url_lis = [('/login/', login),
               ('/register/', register),
               ('/home/', home),
               ('/web/', web),
               ('/time/', nowtime),
               ('/userinfo/',userinfo)
               ]
    def run(environ, start_response):
        # 设置HTTP响应的状态ma码和头信息(指定返回类型和编码格式)
        start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
        # 获取用户访问的url 本质返回跟前例子一样如/time/
        url = environ['PATH_INFO']
        print(url)
        for i in url_lis:
            if url == i[0]:
                fun = i[1]
                msg = fun()
                break
        # 如果没有则发送错误信息
        else:
            msg = b'404 not found'
        # wsgire模块将msg返回给客户,底层依旧是socket
        return [msg, ]
    
    
    if __name__ == '__main__':
        httpd = make_server('127.0.0.1', 8080, run)
        print('等待连接')
        httpd.serve_forever()
    
  • 相关阅读:
    get pc name in LAN
    study spring
    android install
    【转】Java:Session详解
    classic problem: producer and consumer
    tomcat install
    验证Monitor.PulseAll功效
    (转)Windows7的图形架构与DX的那点事
    Cannot open your default email folders. The attempt to log on to Microsoft Exchange has failed.
    Monitor.Wait初探(8)
  • 原文地址:https://www.cnblogs.com/Kingfan1993/p/9845513.html
Copyright © 2020-2023  润新知