• 自创Web框架之过度Django框架


    自创Web框架之过度Django框架

    img

    Web框架,其实就是Web应用的建立;比如网页版的QQ,b站····都是Web应用软件;

    Web应用又是什么?可以理解为基于浏览器的一些应用程序,用户只需要有浏览器即可,不需要再安装其他软件;

    比如我们打开一个URL,Web服务器返回一个HTML页面给你,那么你在搜索或者URL拼接路径搜索的时候Web服务器是怎么知道要返回什么给你?下面介绍一下:

    在介绍之前先介绍所需基础知识,同时也是Web运行所需的:

    👉[Web基础知识](Web入门 - HammerZe - 博客园 (cnblogs.com))

    软件开发架构

    • C/S架构:客户端和服务端
    • B/S架构:浏览器和服务端

    ps:b/s本质也是c/s架构

    HTTP协议

    四大特性

    • 基于TCP、IP作用于应用层之上的协议
    • 基于请求响应
    • 无状态
    • 无(短)连接
      • 长连接:websocket

    数据格式

    请求首行(http协议版本,网络请求的方法)
       请求头(一大堆k,v键值对)
       /r/n   # 换行符不能省略
       请求体(存放的是一些数据,并不是每种请求方式都有请求体,get没有请求体,post有请求体)
        
    # 请求方式
    get:朝服务器索要数据,比如输入网址获得相应的数据
    post:向服务器提交数据,比如用户登录输入用户名和密码后,提交到后端做身份校验
    

    image

    响应格式

    响应首行(http协议版本,网络请求的方法)
      响应头(一大堆k,v键值对)
       /r/n   # 换行符不能省略
      响应体(交给给浏览器展示给用户看的数据)
    

    image

    响应状态码

    HTTP 状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型。响应分为五类:信息响应(100–199),成功响应(200–299),重定向(300–399),客户端错误(400–499)和服务器错误 (500–599)

    分类 分类描述
    1** 信息,服务器收到请求,需要请求者继续执行操作
    2** 成功,操作被成功接收并处理
    3** 重定向,需要进一步的操作以完成请求
    4** 客户端错误,请求包含语法错误或无法完成请求
    5** 服务器错误,服务器在处理请求的过程中发生了错误
    # 注意
    公司还会自定义状态码 一般以10000开头
    参考:聚合数据
    

    请求方式

    • get请求:向别人(服务器)索要数据
    • post请求:向别人提交数据(比如表单)

    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) # 忽略favicon.ico,拿到用户在url后面输入的字符串
        data = data.decode('utf-8')
        conn.send(b'HTTP/1.1 200 Ok\r\n\r\n')
        # 获取字符串中特定的内容   正则和切割
        current_path = data.split(' ')[1]
        # print(current_path)  # /index
        if current_path =='/index':
            with open(r'E:\web组件\图书管理系统界面\图书管理系统.html','rb')as f:
                conn.send(f.read())
        elif current_path == '/login':
            conn.send(b'hello')
        else:
            conn.send(b'Nothing to give you!')
        conn.close()
    

    image

    image

    通过上面的例子,能够简单的理解通过get请求方式得到的页面是如何返回,但是我们个人手写得服务端存在问题,如果客户请求不同得页面那么我们就得写n多个if/else,数据得格式处理起来也比较繁琐······通过一个模块来解决这些问题wsgiref模块

    Web框架之通过wsgiref加油干

    # 解决了上述两个问题
    from wsgiref.simple_server import make_server
    
    
    def run(request,response):
        """
        :param request:请求相关的所有数据
        :param response:响应相关的所有数据
        :return:返回给浏览器的数据
        """
        response('200 OK',[])
        current_path = request.get("PATH_INFO")
        if current_path == '/index':
            return [b'index']
        elif current_path == '/login':
            return [b'login']
        return [b'404 error']
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1',8080,run)  # 实时监听,一旦被访问会全部交给run函数处理
        server.serve_forever()  # 启动服务端
    

    封装优化处理#

    封装的过程中主要解决:

    网址的匹配问题

    网址多匹配如何解决

    功能复杂代码块如何解决,放在一个文件结构不清晰

    '''服务端'''
    from wsgiref.simple_server import make_server
    from views import *
    from urls import *
    '''
    urls.py 路由与视图函数的对应关系
    views.py主要存视图函数
    templates 文件夹主要存HTML文件
    拆分功能后只需在urls.py中书写对应关系,views.py中书写后端的业务逻辑即可
    '''
    
    
    def run(env, response):
        '''
    
        :param env:请求相关的所有数据
        :param response:响应相关的所有数据
        :return:返回给浏览器的数据
        '''
        # print(env)  # wsgiref 模块 将http格式的数据处理好
        response('200 ok',[])
        # 从env返回的大字典中拿出用户输入的内容 --->key
        current_path = env.get('PATH_INFO')
        # if current_path == '/index':
        #     return [b'Hell index']
        # elif current_path == 'login':
        #     return [b'hello wsgiref']
        # else:
        #     return [b'404']
    
        # 定义一个变量存储匹配到的函数名
        func = None
        for url in urls:
            if current_path == url[0]:
                # 匹配成功将url对应的函数名赋值给func
                func = url[1]
                break  # 结束当前循环
            # 判断一下func是否有值
        if func:
            res = func()
        else:
            res = error()
        return [res.encode('utf-8')]
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8081, run)  # 实时监听,只要有客户端来了,就交给run函数执行
        server.serve_forever()  # 启动服务端
    
    
    '''
    urls.py 路由与视图函数的对应关系
    
    
    '''
    from views import *
    
    urls = [
        ('/index',index),
        ('login',login)
    ]
    
    '''
    views.py主要存视图函数
    下面统称为视图函数
    
    '''
    
    
    def index(env):
        return 'index'
    def login(env):
        return 'login'
    def error(env):
        return '404'
    # 返回文件
    def file(env):
        with open(r'E:\web组件\图书管理系统界面\图书管理系统.html','r',encoding='utf8')as f:
            return f.read()
    

    动静网页

    • 静态网页:数据是不变的,不需要实时变化的,数据写死··
    • 动态网页:数据来源于后端(代码、数据库),数据实时变化等特点

    示例一:将时间同步到html页面

    '''服务端'''
    from wsgiref.simple_server import make_server
    
    from urls import *
    def run(env, response):
        '''
    
        :param env:请求相关的所有数据
        :param response:响应相关的所有数据
        :return:返回给浏览器的数据
        '''
        # print(env)  # wsgiref 模块 将http格式的数据处理好
        response('200 ok', [])
        # 从env返回的大字典中拿出用户输入的内容 --->key
        current_path = env.get('PATH_INFO')
        
        # 定义一个变量存储匹配到的函数名
        func = None
        for url in urls:
            if current_path == url[0]:
                # 匹配成功将url对应的函数名赋值给func
                func = url[1]
                break  # 结束当前循环
            # 判断一下func是否有值
        if func:
            res = func(env)
        else:
            res = error(env)
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8081, run)  # 实时监听,只要有客户端来了,就交给run函数执行
        server.serve_forever()  # 启动服务端
    
        
        
    '''urls.py'''
    from views import *
    
    urls = [
        ('/get_time',get_time),
    ]
    
    '''views.py'''
    # 同步时间
    def get_time(env):
        from datetime import datetime
        current_time = datetime.now().strftime('%Y-%m-%d %X')
        with open(r'get_time.html','r',encoding='utf8') as f:
            data = f.read()
        data = data.replace('AAA',current_time)  # 用替换的方式将数据传到HTML文件中
        return data
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <style>
        #d1{
            font-size: 48px;
            color: tomato;
    
        }
    </style>
    <body>
    
    <div class="container">
        <div class="row">
            <div id="d1" class="col-md-8 col-lg-offset-2">
                AAA
            </div>
        </div>
    </div>
    </body>
    </html>
    

    jinjia2模块

    该模块是flask框架必备的模块,在这里只使用jinjia2模块来写我们的模板语法

    需求:将后端字典展示到HTML页面上

    '''服务端和上面一样'''
    
    '''urls.py'''
    from views import *
    urls = [
        ('/get_dict',get_dict)
        ]
    '''views.py'''
    
    # 获取字典
    def get_dict(env):
        user_dict = {'id': 1, 'name': 'Hammer', 'hobby': 'python'}
        from jinja2 import Template
        with open(r'templates/get_dict.html', 'r', encoding='utf8') as f1:
            data = f1.read()
        temp = Template(data)
        res = temp.render(user_data=user_dict)  # 将user_dict传递给html页面,在该页面使用user_data调用
        return res
    
    <!--由于导入了jinjia2模块,这里可以直接使用模板语法,类似python的字典方法-->
    <body>
    {{user_data}}
    {{user_data.id}}
    {{user_data['name']}}
    {{user_data.get('hobby')}}
    </body>
    

    数据库

    需求:操作MySQL数据并且展示到HTML页面上

    注意:数据是在后端处理完之后发送到前端的

    '''服务端'''
    from wsgiref.simple_server import make_server
    
    from urls import *
    def run(env, response):
        '''
    
        :param env:请求相关的所有数据
        :param response:响应相关的所有数据
        :return:返回给浏览器的数据
        '''
        # print(env)  # wsgiref 模块 将http格式的数据处理好
        response('200 ok', [])
        # 从env返回的大字典中拿出用户输入的内容 --->key
        current_path = env.get('PATH_INFO')
        
        # 定义一个变量存储匹配到的函数名
        func = None
        for url in urls:
            if current_path == url[0]:
                # 匹配成功将url对应的函数名赋值给func
                func = url[1]
                break  # 结束当前循环
            # 判断一下func是否有值
        if func:
            res = func(env)
        else:
            res = error(env)
        return [res.encode('utf-8')]
    
    
    if __name__ == '__main__':
        server = make_server('127.0.0.1', 8081, run)  # 实时监听,只要有客户端来了,就交给run函数执行
        server.serve_forever()  # 启动服务端
    
    '''路由'''
    from views import *
    urls = [
            ('/get_db',get_db)
    ]
    
    '''视图函数'''
    # 获取数据库的内容
    def get_db(env):
        import pymysql
        conn = pymysql.connect(
            user='root',
            password='7410',
            port=3306,
            host='127.0.0.1',
            database='info',
            charset='utf8',
            autocommit=True
        )
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        sql = 'select * from emp;'
        rows = cursor.execute(sql)
        res = cursor.fetchall() # [{},{},{}]
        # print(res)
        with open(r'templates/get_db.html','r',encoding='utf8') as f:
            data = f.read()
        temp = Template(data)
        total_res = temp.render(data_list = res)
        return total_res
    
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
        <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
        <script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
    </head>
    <body>
    <div class="container">
        <div class="row">
            <h1 style="text-align: center">用户数据</h1>
            <div class="col-md-8 col-lg-offset-2">
                <table class="table table-striped table-hover ">
                    <thead>
                        <tr class="success" style="text-align: center">
                            <td>id</td>
                            <td>name</td>
                            <td>sex</td>
                            <td>age</td>
                            <td>dep_id</td>
                        </tr>
                    </thead>
                    <tbody>
                        {%for user_dict in data_list%}
                        <tr class="info" style="text-align: center">
                            <td>{{user_dict.id}}</td>
                            <td>{{user_dict.name}}</td>
                            <td>{{user_dict.sex}}</td>
                            <td>{{user_dict.age}}</td>
                            <td>{{user_dict.dep_id}}</td>
                        </tr>
                        {%endfor%}
                    </tbody>
                </table>
            </div>
        </div>
    </div>
    </body>
    </html>
    

    image

    到此前后端和数据库交互就都完成了,这所有的功能都可以用Django完成,上面只是一个推导过程~

    image


    自写框架梳理

    • wsgiref模块:
      • 封装了socket代码
      • 处理了http数据格式
    • 根据功能的不同拆分成不同的文件夹
      • urls.py 路由与视图函数对应关系
      • views.py 视图函数
      • templates 模板文件夹
    • 步骤:
      • 第一步添加路由与视图函数的对应关系
      • 去views中书写功能代码
      • 如果需要使用到html则去模板文件夹中操作
    • jinjia2模板语法
      • {{}}
      • {%%}

  • 相关阅读:
    PriorityQueue类的使用及底层原理
    CSO面对面丨如何通过“联合作战”,加强银行安全体系建设
    算法---分割字符串的方案数
    【ARM】关于指令集架构?
    夜天之书 #67 为什么开源协议不授予商标权利?
    <C++>文件操作基础详解,快来写出你的第一个文件吧
    基于java(ssm)大学生社团管理系统源码成品(java毕业设计)
    FFplay文档解读-35-视频过滤器十
    让文字在盒子中水平居中与垂直居中
    电力系统规划学习笔记(长期更新)
  • 原文地址:https://www.cnblogs.com/48xz/p/15929259.html