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

    Web框架的本质

    web应用

      众所周知,对于所有的web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端

    import socket
      
    def handle_request(client):
        buf = client.recv(1024)
        client.send("HTTP/1.1 200 OK
    
    ")
        client.send("Hello, Seven")
      
    def main():
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost',8000))
        sock.listen(5)
      
        while True:
            connection, address = sock.accept()
            handle_request(connection)
            connection.close()
      
    if __name__ == '__main__':
        main()
    View Code

      上述通过socket来实现了其本质,而对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。应用程序则负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。

    WSGI

      Web服务网关接口(Web Server Gateway Interface,简称“WSGI”)是一种在web服务器和Python web应用程序或框架之间的标准接口。具体的来说,WSGI是一种规范,它定义了使用python编写的web app与web server之间接口格式(Web服务器如何与Python应用程序进行交互),使得使用Python写的Web应用程序可以和Web服务器对接起来,实现web app与web server间的解耦。

      通过标准化web服务器和Python web应用程序或框架之间的行为和通信,WSGI使得编写可移植的的Python web代码变为可能,使其能够部署在任何 WSGI可用的 web 服务器 上。WSGI的文档在 PEP 3333

    关于wsgi的更多信息:https://segmentfault.com/a/1190000003069785

      python标准库提供的独立WSGI服务器称为wsgiref。

    from wsgiref.simple_server import make_server
    
    def RunServer(environ, start_response):
        """
        :param environ: 包含用户请求的所有信息,是wsgi封装好的
        :param start_response: 一个发送HTTP响应的函数
        """
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [bytes('<h1>Hello, web!</h1>', encoding='utf-8'), ]
    
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print("Serving HTTP on port 8000...")
        httpd.serve_forever()
    
    """
    详解environ, start_response参数
    
    environ参数
    environ参数是一个Python的字典,里面存放了所有和客户端相关的信息,这样application对象就能知道客户端请求的资源是什么,请求中带了什么数据等。environ字典包含了一些CGI规范要求的数据,以及WSGI规范新增的数据,还可能包含一些操作系统的环境变量以及Web服务器相关的环境变量。我们来看一些environ中常用的成员:
    
    首先是CGI规范中要求的变量:
    
    REQUEST_METHOD: 请求方法,是个字符串,'GET', 'POST'等
    SCRIPT_NAME: HTTP请求的path中的用于查找到application对象的部分,比如Web服务器可以根据path的一部分来决定请求由哪个virtual host处理
    PATH_INFO: HTTP请求的path中剩余的部分,也就是application要处理的部分
    QUERY_STRING: HTTP请求中的查询字符串,URL中?后面的内容
    CONTENT_TYPE: HTTP headers中的content-type内容
    CONTENT_LENGTH: HTTP headers中的content-length内容
    SERVER_NAME和SERVER_PORT: 服务器名和端口,这两个值和前面的SCRIPT_NAME, PATH_INFO拼起来可以得到完整的URL路径
    SERVER_PROTOCOL: HTTP协议版本,HTTP/1.0或者HTTP/1.1
    HTTP_: 和HTTP请求中的headers对应。
    WSGI规范中还要求environ包含下列成员:
    
    wsgi.version:表示WSGI版本,一个元组(1, 0),表示版本1.0
    wsgi.url_scheme:http或者https
    wsgi.input:一个类文件的输入流,application可以通过这个获取HTTP request body
    wsgi.errors:一个输出流,当应用程序出错时,可以将错误信息写入这里
    wsgi.multithread:当application对象可能被多个线程同时调用时,这个值需要为True
    wsgi.multiprocess:当application对象可能被多个进程同时调用时,这个值需要为True
    wsgi.run_once:当server期望application对象在进程的生命周期内只被调用一次时,该值为True
    上面列出的这些内容已经包括了客户端请求的所有数据,足够application对象处理客户端请求了。
    
    start_resposne参数
    start_response是一个可调用对象,接收两个必选参数和一个可选参数:
    
    status: 一个字符串,表示HTTP响应状态字符串
    response_headers: 一个列表,包含有如下形式的元组:(header_name, header_value),用来表示HTTP响应的headers
    exc_info(可选): 用于出错时,server需要返回给浏览器的信息
    当application对象根据environ参数的内容执行完业务逻辑后,就需要返回结果给server端。我们知道HTTP的响应需要包含status,headers和body,所以在application对象将body作为返回值return之前,需要先调用start_response(),将status和headers的内容返回给server,这同时也是告诉server,application对象要开始返回body了。
    """
    View Code

    Web 框架

      Web 框架就是一份代码库,它能帮助开发人员创建可靠、可扩展、易维护的 Web 应用。

    广义地说,web框架包含一系列库和一个主要的handler,这样你就能够构建自己的代码来实现web应用(比如说一个交互式的网站)。大多数web框架包含模式和工具,至少实现以下功能:

    URL路由(URL Routing)
    将输入的HTTP请求匹配到特定的Python代码用来调用
    请求和响应对象(Request and Response Objects)
    封装来自或发送给用户浏览器的信息
    模板引擎(Template Engine)
    能够将实现应用的Python代码逻辑和其要产生输出的HTML(或其他)分离开
    web服务器开发(Development Web Server)
    在开发机上运行HTTP服务器,从而快速开发;当文件更新时自动更新服务端代码。

    通过python标准库提供的wsgiref模块开发一个自己的Web框架

    from wsgiref.simple_server import make_server
    
    def index():
        return 'index'
    
    def login():
        return 'login'
    
    def routers():
        # 路由函数
        urlpatterns = (
            ('/index/', index),
            ('/login/', login),
        )
    
        return urlpatterns
    
    def RunServer(environ, start_response):
        # 响应头
        start_response('200 OK', [('Content-Type', 'text/html')])
        # 获取客户端url信息
        url = environ['PATH_INFO']
        # 执行路由函数,获取路由元组
        urlpatterns = routers()
        func = None
        for item in urlpatterns:
            # 如果用户请求的url和我们定义的url匹配,将路由的url所对应的函数赋给func
            if item[0] == url:
                func = item[1]
                break
        if func:
            return func()
        # 执行里面的方法
        else:
            return '404 not found'
    
    
    if __name__ == '__main__':
        httpd = make_server('', 8000, RunServer)
        print(    "Serving HTTP on port 8000...")
        httpd.serve_forever()
    View Code

    模板

    多数WSGI应用响应HTTP请求,从而服务于HTML或其他标记语言中的内容。关注点分离的概念建议我们使用模板,而不是直接由Python生成文本内容。模板引擎管理一系列的模板文件,其系统的层次性和包容性避免了不必要的重复。模板引擎负责渲染(产生)实际内容,用由应用生成的动态内容填充静态内容。

    由于模板文件有时是由设计师或者前端开发者编写,处理不断增长的复杂度会变得困难。

    一些通用的良好实践应用到了部分应用中,情景包括传递动态内容到模板引擎和模板自身中。

    • 模板文件只应传递需要渲染的动态内容。避免传递附加的“以防万一”的内容: 需要时添加遗漏的变量比移除可能不用的变量要来的容易。
    • 许多模板引擎允许在模板中编写复杂语句或者赋值,也有许多允许一些Python代码 在模板中等价编写。这种便利会导致复杂度不可控地增加,也使得查找bug变得更加 困难。
    • 我们常常需要混合JavaScript模板和HTML模板。一种聪明的做法是孤立出HTML 模板传递部分变量内容到JavaScript代码中的部分。

    Jinja2

    Jinja2 是一个和Django模板系统类似的模板引擎,并有一些额外的特性。它是一种基于文本的模板语言,因此能够用于生成任何标记内容。它允许定制过滤(filter)、标签、测试和全局内容,不像在Django框架中实现的模板系统,它还允许调用函数

    遵循jinja2的语法规则,其内部会对指定的语法进行相应的替换,从而达到动态的返回内容

     在Jinja2中重要的html标签:

    {# 这是注释 #}
    
    {# 下一个标签是输出变量: #}
    {{title}}
    
    {# 区块标签,能通过继承其他html代码来替换区块内容 #}
    {% block head %}
    <h1>This is the head!</h1>
    {% endblock %}
    
    {# 数组迭代输出 #}
    {% for item in list %}
    <li>{{ item }}</li>
    {% endfor %}

    注意“{% ... %}"与"{{ .. }}", 前者用来执行一个循环或者一个赋值语句,后者用来打印一个变量。 

    from wsgiref.simple_server import make_server
    from jinja2 import Template
    
    def index():
        # return 'index'
    
        # template = Template('Hello {{ name }}!')
        # result = template.render(name='John Doe')
    
        f = open('index.html')
        result = f.read()
        template = Template(result)
        data = template.render(name='John Doe', user_list=['alex', 'eric'])
        return data.encode('utf-8')
    
    def login():
        # return 'login'
        f = open('login.html')
        data = f.read()
        return data
    
    def routers():
        urlpatterns = (
            ('/index/', index),
            ('/login/', login),
        )
    
        return urlpatterns
    
    
    def run_server(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        url = environ['PATH_INFO']
        urlpatterns = routers()
        func = None
        for item in urlpatterns:
            if item[0] == url:
                func = item[1]
                break
        if func:
            return func()
        else:
            return '404 not found'
    
    
    if __name__ == '__main__':
        httpd = make_server('', 8000, run_server)
        print("Serving HTTP on port 8000...")
        httpd.serve_forever()
    demo
    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>{{name}}</h1>
    
        <ul>
            {% for item in user_list %}
            <li>{{item}}</li>
            {% endfor %}
        </ul>
    
    </body>
    </html>
    
    index.html
    index.html

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

     参考:http://www.cnblogs.com/wupeiqi/articles/5237672.html

     参考:http://pythonguidecn.readthedocs.io/zh/latest/scenarios/web.html#

  • 相关阅读:
    Java8新特性一览表
    FastDFS 单机部署指南
    EntityManager的Clear方法的使用
    元类
    python中的函数的分类
    python中的多任务
    正则表达式
    GIL和copy
    文件管理
    logging日志模块配置
  • 原文地址:https://www.cnblogs.com/freely/p/6679792.html
Copyright © 2011-2022 走看看