• 认识Web应用框架


    Web应用框架

    Web应用框架(Web application framework)是一种开发框架,用来支持动态网站、网络应用程序及网络服务的开发。类型可以分为基于请求(request-based)的和基于组件(component-based)的两种Web框架。(--来源:百度词条 )

    应用:有助于减轻网页开发时共通性活动的工作负荷,例如很多框架提供数据库访问接口、标准样板以及会话管理等,可提升代码的可再用性。(--来源:百度词条 )

    Web应用本质上就是一个socket服务端,用户的浏览器是一个socket客户端,基于此,可自定义一个简易版Web框架

    '''
    自定义的简易版Web框架
    '''
    import socket
    from socket import SOL_SOCKET
    from socket import SO_REUSEADDR
    
    # 获取socket对象server
    server = socket.socket()
    # 允许该端口可以多次运行
    server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
    # 设置服务端的IP和端口
    server.bind((
        '127.0.0.1',
        9527
    ))
    # 设置半连接池
    server.listen(5)
    
    while True:
        # 等待客户端访问,当前是阻塞状态,直到有客户端访问
        conn, addr = server.accept()
        # 接收客户端发送的数据
        data = conn.recv(1024)
        # 打印客户端发送的请求数据
        print(data)
        # 向客户端发送响应的数据
        conn.send(b'hello world')
        # 关闭连接
        conn.close()
    

    自定义的web框架服务端已完成,通过浏览器访问 127.0.0.1:9527,会发现这个页面根本不能正常工作。

    我们都知道B/S架构基于浏览器访问服务端的时候,需要遵循HTTP协议,因为HTTP协议规定了客户端和服务端之间的通信格式,那么用户在浏览器输入 https:\127.0.0.1:9527,服务端接收到的请求数据什么

    1578050012856

    在此之前,首先学习下HTTP协议

    了解了HTTP协议后,如果想要server端给予响应,必须让server端在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行

    while True:
        # 等待客户端访问,当前是阻塞状态,直到有客户端访问
        conn, addr = server.accept()
        # 接收客户端发送的数据
        data = conn.recv(1024)
        # 在向客户端发送响应数据前,必须加上响应状态行
        # HTTP/1.1:协议版本号   200 OK:状态码,200表示客户端请求成功 
    
    :空行
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        # 向客户端发送响应的数据,即响应体
        conn.send(b'hello world')
        # 关闭连接
        conn.close()
    

    加上响应状态行后,重新访问 https:\127.0.0.1:9527,则浏览器可以收到服务端返回的 hello world

    • 第一次优化

    如果想要Web服务根据用户请求的URL不同,返回不同的内容,实现思路:首先得拿到这个URL,然后根据URL判断,进而返回不同的内容给浏览器

    while True:
        # 等待客户端访问,当前是阻塞状态,直到有客户端访问
        conn, addr = server.accept()
        # 接收客户端发送的数据
        data = conn.recv(1024)
        print(data)
        # 在向客户端发送响应数据前,必须加上响应状态行
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        path_info = data.decode('utf-8').split('
    ')[0].split()[1]
        print(path_info)
        # 根据不同的路径向客户端发送响应的数据
        if path_info == '/index':
            conn.send(b'from index')
        elif path_info == '/login':
            conn.send(b'from login')
        else:
            conn.send(b'404 error')
    
        # 关闭连接
        conn.close()
    

    函数版

    # 访问index页面调用的函数
    def index(url):
        return f'from [{url}]file'
    
    
    # 访问login页面调用的函数
    def login(url):
        return f'from [{url}]file'
    
    
    # 创建访问页面路径和函数的对应关系
    url_list = [
        ('/index/', index),
        ('/login/', login)
    ]
    
    while True:
        # 等待客户端访问,当前是阻塞状态,直到有客户端访问
        conn, addr = server.accept()
        # 接收客户端发送的数据
        data = conn.recv(1024)
        # 在向客户端发送响应数据前,必须加上响应状态行
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        path_info = data.decode('utf-8').split('
    ')[0].split()[1]
        # 根据不同的路径向客户端发送响应的数据
        # if path_info == '/index':
        #     conn.send(b'from index')
        # elif path_info == '/login':
        #     conn.send(b'from login')
        # else:
        #     conn.send(b'404 error')
        # 函数版
        func = None
        # 遍历访问页面路径是否在列表中
        for line in url_list:
            if line[0] == path_info:
                func = line[1]
        
        # 请求页面路径不在列表中,返回404响应
        if not func:
            res = '404 error'
        else:
            res = func(path_info)
    
        conn.send(res.encode('utf-8'))
        # 关闭连接
        conn.close()
    
    • 第二次优化

    如果想要根据用户请求的URL不同,返回不同的HTML页面

    # 访问index页面调用的函数
    def index():
        # 读取index.html页面中的内容
        with open(r'E:Oldboypython3200103自定义Web框架	emplatesindex.html',
                  'r', encoding='utf-8') as f:
            data = f.read()
        # return f'from [{url}]file'
        return data
    
    
    # 访问login页面调用的函数
    def login():
        with open(r'E:Oldboypython3200103自定义Web框架	emplateslogin.html',
                  'r', encoding='utf-8') as f:
            data = f.read()
        # return f'from [{url}]file'
        return data
    
    
    # 创建访问页面路径和函数的对应关系
    url_list = [
        ('/index/', index),
        ('/login/', login)
    ]
    
    while True:
        # 等待客户端访问,当前是阻塞状态,直到有客户端访问
        conn, addr = server.accept()
        # 接收客户端发送的数据
        data = conn.recv(1024)
        # 在向客户端发送响应数据前,必须加上响应状态行
        conn.send(b'HTTP/1.1 200 OK 
    
    ')
        path_info = data.decode('utf-8').split('
    ')[0].split()[1]
        # 根据不同的路径向客户端发送响应的数据
        # if path_info == '/index':
        #     conn.send(b'from index')
        # elif path_info == '/login':
        #     conn.send(b'from login')
        # else:
        #     conn.send(b'404 error')
        # 函数版
        func = None
        # 遍历访问页面路径是否在列表中
        for line in url_list:
            if line[0] == path_info:
                func = line[1]
    
        # 请求页面路径不在列表中,返回404响应
        if not func:
            res = '404 error'
        else:
            res = func()
    
        conn.send(res.encode('utf-8'))
        # 关闭连接
        conn.close()
    
    • 第三次优化,不再自己手动创建套接字服务端,使用wsgiref模块
    from wsgiref.simple_server import make_server
    import urls
    import views
    
    
    def run(env, response):
        """
        :param env: 请求相关的所有数据
        :param response: 响应相关的所有数据
        :return: 浏览器能够接受的内容
        """
        response('200 OK', [])
        # print(env)  # 是一个字典,其中PATH_INFO这个键值对存储的就是用户请求的url
        target_url = env.get('PATH_INFO')
        # 定义变量,存储可能匹配的函数,不直接调用是因为有可能请求的路径不存在
        func = None
        for line in urls.url_list:
            if line[0] == target_url:
                # 符合条件,说明后端已设置对应的页面数据,赋给func
                func = line[1]
                # 找到后,跳出循环,没必要继续查找
                break
    	
        # 最后都没有找到,说明请求无效
        if not func:
            # 调用404页面
            res = views.error(env)
        else:
            res = func(env)
    	
        # 将响应页面的数据返回给浏览器,展示给用户
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        # 监听host:port,一旦有客户端(浏览器)访问,立即执行第三个参数(可以是类)
        server = make_server('127.0.0.1', 9527, run)
        # 启动服务端
        server.serve_forever()
    

    了解一下

    参照:

    https://www.leiue.com/what-is-wsgi

    https://blog.csdn.net/laughing2333/article/details/51288660

    https://cizixs.com/2014/11/09/dive-into-wsgiref/

    WSGI和 wsgiref

    WSGI(Web Server Gateway Interface) Web服务器网关接口,是专门为Python语言定义的web服务器与Web应用程序或框架之间的一种简单而通用的接口

    wsgiref 是一个实现了WSGI标准的范例实现(用于演示的简单python内置库),里面的功能包含了:1.操作wsgi 的环境变量;2.应答头部的处理;3.实现简单的HTTP server;4.简单的程序端和服务端校验函数

    • 第四次优化 基于jinja2模块,实现动态网页
    # 导入jinja2模块,使用模板语法
    from jinja2 import Template
    import pymysql
    # 使用jinja2 中的模板语法实现动态网页
    def userinfo(env):
        # 调用数据库中的记录
        conn = pymysql.connect(
            host='127.0.0.1',
            port=3306,
            user='root',
            password='Ad123',
            database='django_test',
            charset='utf8',
            autocommit=True
        )
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        sql = 'select * from userinfo'
        cursor.execute(sql)
        # 返回结果集:[{},{},{}]
        data = cursor.fetchall()
        
        # 该函数返回一个html页面
        with open(r'E:Oldboypython3200103自定义Web框架	emplatesuserinfo.html',
                  'r', encoding='utf-8') as f:
            res = f.read()
    
        # 利用jinja2
        tmp = Template(res)
        # 利用对象的render 方法,将从数据库的结果集传给html页面
        res = tmp.render(xxx=data)
        return res
    
    <table class="table table-striped table-hover">
        <thead>
        <tr>
            <th>序号</th>
            <th>username</th>
            <th>password</th>
        </tr>
        </thead>
        <tbody>
        <!--通过jinja2的模板语法,可以直接在html文档中使用python 语法-->
        <!--获取到数据集[{},{},{}],遍历结果集-->
        {%for user_dict in xxx %}
        <!--一个字典(数据库的中一条数据)就是一行记录-->
        <tr>
            <!--字典中的键值对就是一列-->
            <td>{{ user_dict.id }}</td>
            <td>{{ user_dict.username }}</td>
            <td>{{ user_dict.password }}</td>
        </tr>
        {% endfor %}
        </tbody>
    </table>*
    

    了解一下

    参照: https://baike.baidu.com/item/jinja2/8911090?fr=aladdin

    http://docs.jinkan.org/docs/jinja2/

    https://www.jianshu.com/p/f04dae701361

    jinja2:基于python的模板引擎,其设计思想来源于django的模板引擎,并扩展了其语法和一系列强大的功能。其中最显著的一个是增加了沙箱执行功能和可选的自动转义功能。

    特点:

    • 沙箱中执行
    • 强大的HTML自动转义系统,可以有效地组织跨站脚本攻击(XSS,利用网站漏铜从用户那里恶意盗取信息)
    • 模板继承机制,此机制可以使得所有的模板都具有相似一致的布局, 方便了开发人员对模板的修改和管理
    • 高效的执行效率,Jinja2引擎在模板第一次加载时就把源码转换成Python字节码,加快模板执行时间。
    • 可选的预编译模式
    • 易于调试。异常的行数直接指向模板中的对应行
    • 可配置的语法

    语法

    • 控制结构 {% %}
    • 变量取值 {{ }}
    jinja2模板中使用{{ }} 语法表示一个变量,是一种特殊的占位符。当利用jinja2进行渲染的时候,它会把这些特殊的占位符进行填充/替换,jinja2支持python中所有数据类型,如列表、字段、对象等
    
    • 注释 {# #}
  • 相关阅读:
    eclipse不能反编译 eclipse中Source not found解决方法
    3. copy 使用及示例
    pgbench 使用介绍及示例
    1. 将 TDSQL 接入已经在使用的 HDFS
    1.PGbadger 慢日志分析工具
    1. pg_dump 使用及示例
    5.6.10 HDFS DN 节点扩容
    关于 PG CVE202221724 漏洞处理
    2.pg_dumpall 使用及示例
    4.pg_bulkload 数据加载使用及示例
  • 原文地址:https://www.cnblogs.com/xiaodan1040/p/12152236.html
Copyright © 2020-2023  润新知