zoukankan      html  css  js  c++  java
  • Web框架的本质

    1. Web请求和响应的过程

    简单来说,服务端接收到用户访问网站的请求时,无非就是将用户发来的请求信息进行分析(请求头+请求体)。

    再根据用户请求信息中内容在服务端做对应的处理后,将处理后的结果(字符串)作为响应体,再加上响应头后构建成完整的响应报文返回给客户端。

    如我们可以根据请求头中的URL来加载对应的文件进行响应,而这个加载对应文件的过程我们可以做成一个函数,从而可以根据请求头中URL信息来执行不同的函数进行处理。

    而对于处理的过程,我们可以是直接加载一个html文件后,将整个html文件中的内容作为一个字符串加入响应体中后构建响应报文,再直接发送给客户端。这就是静态资源的请求过程。

    而对于变化的数据(动态资源),我们还需要从数据库中拿到对应的数据后,将这些数据填充进html文件中的指定位置,再将填充后的内容作为一个字符串附着进响应体中,再构建响应报文发送给客户端。

    这时,不难发现,现在的这个html文件,已然变成了一个模板,我们可以在这个模板上定义特殊的替换规则后,就可以填充我们想要添加的任何内容。这样就可以实现内容的动态显示。

    2. 一个简单的静态Web服务器

    根据以上基本原理,我们不难写出一个根据请求的URL来响应对应html网页内容的简单的静态资源web服务器。

    import socket
    
    def f1(request):
        # request中包含了用户请求的所有内容:请求头+请求体
        f = open('index.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    def f2(request):
        f = open('article.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    routers = [
        ('/f1', f1),
        ('/f2', f2),
    ]
    
    def run():
        sock = socket.socket()
        sock.bind(('127.0.0.1', 8080))
        sock.listen(5)
    
        while True:
            # 等待客户端来连接
            conn, addr = sock.accept()
            # 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
            data = conn.recv(8096)
    
            """对用户发来的信息进行提取"""
    
            # 将内容转化成字符串类型
            data = str(data, encoding='utf-8')
            # data = bytes('shit', encoding='utf-8')
    
            # 将内容分割成请求头和请求体
            headers, bodys = data.split('
    
    ')
            # 得到请求头中的每一行数据
            temp_list = headers.split('
    ')
            # 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
            method, url, protocol = temp_list[0].split(' ')
    
            """根据用户请求的URL来返回不同的内容"""
            """思路:
                遍历routers中的每个元组的第一个值和请求头中的url进行比较,
                若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
            """
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
            if func_name:
                response = func_name(data)  # 把data中的内容传进去
            else:
                response = b"404 not found"
    
            conn.send(response)
            conn.close()
    
    
    if __name__ == '__main__':
            run()

    3. 动态网站Web服务器

    动态web无非就是数据从数据库来提取,所以数据是会变化的,而非像静态页面那样一成不变。

    import socket
    import pymysql
    
    # 静态页面
    def index(request):
        # request中包含了用户请求的所有内容:请求头+请求体
        f = open('index.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    # 静态页面
    def article(request):
        f = open('article.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    # 动态页面
    def user_info(request):
    
        # 连接数据库并取得数据
        mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
        conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
        ret = conn_cursor.execute("select name,age,gender from first_test")
        print("受影响的行数:%s" % ret)
        user_list = conn_cursor.fetchall()
        conn_cursor.close()
        mysql_conn.close()
        print("打印从数据库中拿到的内容: %s" % user_list)
    
        # 将从数据库中拿到的内容进行拼接
        content_list = []
        for row in user_list:
            tp = "<tr><th>%s</th><th>%s</th><th>%s</th></tr>" % (row["name"], row["age"], row["gender"])
            content_list.append(tp)
        user_content = "".join(content_list)
    
        # 将html文件中指定位置的内容替换成数据库中取得后拼接好的内容
        f = open("user_list.html", "r", encoding='utf-8')
        html_template = f.read()
        f.close()
    
        # 替换为指定内容
        # 这里其实就是最简单的所谓的模板的渲染
        data = html_template.replace("@@content@@", user_content)
    
        return bytes(data, encoding="utf-8")
    
    
    routers = [
        ('/index.html', index),
        ('/article.html', article),
        ('/user_info.html', user_info),
    ]
    
    def run():
        sock = socket.socket()
        sock.bind(('127.0.0.1', 8080))
        sock.listen(5)
    
        while True:
            # 等待客户端来连接
            conn, addr = sock.accept()
            # 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
            data = conn.recv(8096)
    
            """对用户发来的信息进行提取"""
    
            # 将内容转化成字符串类型
            data = str(data, encoding='utf-8')
            # data = bytes('shit', encoding='utf-8')
    
            # 将内容分割成请求头和请求体
            headers, bodys = data.split('
    
    ')
            # 得到请求头中的每一行数据
            temp_list = headers.split('
    ')
            # 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
            method, url, protocol = temp_list[0].split(' ')
    
            """根据用户请求的URL来返回不同的内容"""
            """思路:
                遍历routers中的每个元组的第一个值和请求头中的url进行比较,
                若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
            """
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
            if func_name:
                response = func_name(data)  # 把data中的内容传进去
            else:
                response = b"404 not found"
    
            conn.send(response)
            conn.close()
    
    
    if __name__ == '__main__':
            run()
    

    4. 用Jinja2进行模板渲染

    这时不难发现,如果我们手动的对html模板进行内容替换,这将会非常麻烦,然而已经有这样的模板渲染工具了。我们可以按照Jinja2规定要的语法来对我们的html模板进行渲染。

    import socket
    import pymysql
    
    # 静态页面
    def index(request):
        # request中包含了用户请求的所有内容:请求头+请求体
        f = open('index.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    # 静态页面
    def article(request):
        f = open('article.html', 'rb')
        data = f.read()
        f.close()
        return data
    
    # 动态页面,手动渲染
    def user_info(request):
    
        # 连接数据库并取得数据
        mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
        conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
        ret = conn_cursor.execute("select name,age,gender from first_test")
        print("受影响的行数:%s" % ret)
        user_list = conn_cursor.fetchall()
        conn_cursor.close()
        mysql_conn.close()
        print("打印从数据库中拿到的内容: %s" % user_list)
    
        # 将从数据库中拿到的内容进行拼接
        content_list = []
        for row in user_list:
            tp = "<tr><th>%s</th><th>%s</th><th>%s</th></tr>" % (row["name"], row["age"], row["gender"])
            content_list.append(tp)
        user_content = "".join(content_list)
    
        # 将html文件中指定位置的内容替换成数据库中取得后拼接好的内容
        f = open("template_shit.html", "r", encoding='utf-8')
        html_template = f.read()
        f.close()
    
        # 替换为指定内容
        # 这里其实就是最简单的所谓的模板的渲染
        data = html_template.replace("@@content@@", user_content)
    
        return bytes(data, encoding="utf-8")
    
    # 动态页面,用jinja2进行模板渲染
    def template_shit(request):
        # 连接数据库并取得数据
        mysql_conn = pymysql.connect(host='10.0.0.204', port=3306, user='hgzero', passwd='woshiniba', db='my_test')
        conn_cursor = mysql_conn.cursor(cursor=pymysql.cursors.DictCursor)
        conn_cursor.execute("select name,age,gender from first_test")
        user_list = conn_cursor.fetchall()
        conn_cursor.close()
        mysql_conn.close()
    
        # 获取html模板的内容
        f = open("template_shit.html", "r", encoding='utf-8')
        html_data = f.read()
        f.close()
    
        # 用jinja2进行模板渲染
        from jinja2 import Template
        template = Template(html_data)
        # 用从数据库中拿到的user_list来替换模板中的user_list变量
        data = template.render(user_list=user_list)
        # 模板中定义的规则:
        """{% for rwo in user_list %}
                      <tr>
                          <td>{{row.name}}</td>
                          <td>{{row.age}}</td>
                          <td>{{row.gender}}</td>
                      </tr>
                  {% endfor %}
        """
        print(data)  # 看看模板生成的内容
        return data.encode("utf-8")
    
    
    routers = [
        ('/index.html', index),
        ('/article.html', article),
        ('/user_info.html', user_info),
        ('/template_shit.html', template_shit),
    ]
    
    def run():
        sock = socket.socket()
        sock.bind(('127.0.0.1', 8080))
        sock.listen(5)
    
        while True:
            # 等待客户端来连接
            conn, addr = sock.accept()
            # 获取用户发送的数据,收到的数据是Bytes,(二进制格式)
            data = conn.recv(8096)
    
            """对用户发来的信息进行提取"""
    
            # 将内容转化成字符串类型
            data = str(data, encoding='utf-8')
            # data = bytes('shit', encoding='utf-8')
    
            # 将内容分割成请求头和请求体
            headers, bodys = data.split('
    
    ')
            # 得到请求头中的每一行数据
            temp_list = headers.split('
    ')
            # 对请求头中的第一行数据进行提取,取得请求方法、请求URL、请求方法
            method, url, protocol = temp_list[0].split(' ')
    
            """根据用户请求的URL来返回不同的内容"""
            """思路:
                遍历routers中的每个元组的第一个值和请求头中的url进行比较,
                若匹配成功,则将对应的函数名赋值给func_name,进而去执行该URL对应的函数
            """
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
            if func_name:
                response = func_name(data)  # 把data中的内容传进去
            else:
                response = b"404 not found"
    
            conn.send(response)
            conn.close()
    
    
    if __name__ == '__main__':
            run()
    

    至此,可以看到,现在它已经基本上实现的一个框架应该实现的功能。

    5. Web框架的种类

    5.1 Web框架应该实现的功能

    1. Socket服务端
    2. 路由系统:根据URL来返回不同的内容,URL --> 函数
    3. 字符串加入响应体返回给用户:模板引擎渲染,字符串

    5.2 Web框架的种类

    根据以上的三种主要功能,web框架可以分为以下几类

    • 自己实现了 1,2,3  
      • Tornado
    • 实现了 2,3 ,而使用了第三方的Socket服务端
      • Django
    • 实现了 2 , 而使用了第三方的Socket服务器以及第三方的模板渲染引擎(如Jinja2)
      • flask

  • 相关阅读:
    前沿技术解密——VirtualDOM
    Ques核心思想——CSS Namespace
    Unix Pipes to Javascript Pipes
    Road to the future——伪MVVM库Q.js
    聊聊CSS postproccessors
    【译】十款性能最佳的压缩算法
    Kafka Streams开发入门(9)
    Kafka Streams开发入门(8)
    【译】Kafka Producer Sticky Partitioner
    【译】99th Percentile Latency at Scale with Apache Kafka
  • 原文地址:https://www.cnblogs.com/hgzero/p/13216511.html
Copyright © 2011-2022 走看看