美文网首页
web框架的本质

web框架的本质

作者: 莫辜负自己的一世韶光 | 来源:发表于2018-11-02 19:16 被阅读0次

    所有的web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,这样我们就可以实现一个web框架了.

    • 半成品自定义的web框架

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

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

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

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

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

    这样访问网页的时候,会出现如下界面:

    这是因为响应的时候,必须按照一定的格式才可以.

    • 可以响应成功的简单版本的自定义的Web框架,带响应行的

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

    这样并不能很好的响应不同的请求,可以根据解析请求的内容,根据请求不同的URL,返回不同的内容.


    • 用函数实现不同的路径,不同的处理方式.可以使用一个列表,不同的路径,对应不同的函数处理方法.
    # encoding:utf-8
    __author__ = 'Fioman'
    __date__ = '2018/11/2 17:08'
    import socket
    
    sock = socket.socket()
    sock.bind(('127.0.0.1', 8000))
    sock.listen()
    
    
    def index(url):
        s = "这是{}页面!".format(url)
        return bytes(s, encoding='utf-8')
    
    
    def home(url):
        s = "这是{}页面!".format(url)
        return bytes(s, encoding='utf-8')
    
    
    func_urls = [
        ('/index/', index),
        ('/home/', home),
    ]
    while True:
        conn, addr = sock.accept()
        data = conn.recv(8096)
        data = str(data, encoding='utf-8')
        requestLine = data.split('\r\n')[0]
        url = requestLine.split(' ')[1]
    
        # 路径和函数的对应关系
        func = None
        for i in func_urls:
            if i[0] == url:
                func = i[1]
                break
    
        if func:
            response = func(url)
        else:
            response = b'404 not found!'
        conn.send(b'HTTP/1.1 200 OK')
        conn.send(response)
    
        conn.close()
    
    sock.close()
    
    

    • 返回不同的HMTL文件

    本质就是读取本地的HTML文件,然后以二进制数据的形式发送给客户端

    # encoding:utf-8
    __author__ = 'Fioman'
    __date__ = '2018/11/2 17:08'
    import socket
    
    sock = socket.socket()
    sock.bind(('127.0.0.1', 8000))
    sock.listen()
    
    
    def index(url):
        with open('index.html', 'rb', encoding='utf-8') as f:
            ret = f.read()
        return ret
    
    
    def home(url):
        with open('home.html', 'rb', encoding='utf-8') as f:
            ret = f.read()
        return ret
    
    
    func_urls = [
        ('/index/', index),
        ('/home/', home),
    ]
    while True:
        conn, addr = sock.accept()
        data = conn.recv(8096)
        data = str(data, encoding='utf-8')
        requestLine = data.split('\r\n')[0]
        url = requestLine.split(' ')[1]
    
        # 路径和函数的对应关系
        func = None
        for i in func_urls:
            if i[0] == url:
                func = i[1]
                break
    
        if func:
            response = func(url)
        else:
            response = b'404 not found!'
        conn.send(b'HTTP/1.1 200 OK')
        conn.send(response)
    
        conn.close()
    
    sock.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部分

    # encoding:utf-8
    __author__ = 'Fioman'
    __date__ = '2018/11/2 17:35'
    import time
    from wsgiref.simple_server import make_server
    
    
    # 将返回不同的内容部分封装为函数
    def index(url):
        with open('index.html', 'r', encoding='utf-8') as f:
            s = f.read()
            now = str(time.time())
            s = s.replace("@@xx@@", now)
        return bytes(s, encoding='utf-8')
    
    
    def home(url):
        with open('home.html', 'rb', encoding='utf-8') as f:
            ret = f.read()
        return ret
    
    
    # 定义一个url和实际要执行的函数的对应关系
    func_urls = [
        ('/index/', index),
        ('/home/', home),
    ]
    
    
    def run_server(environ, start_response):
        # 设置HTTP响应的状态码和头消息
        start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])
        # 取到用户输入的url
        url = environ['PATH_INFO']
        func = None
    
        for i in func_urls:
            if i[0] == url:
                func = i[1]
                break
    
        if func:
            response = func(url)
        else:
            response = b'404 not found'
    
        return [response,]
    
    
    
    if __name__ == '__main__':
        httpd = make_server('127.0.0.1', 8000, run_server)
        print('我在8000端口等你哦.........')
        # 启动服务
        httpd.serve_forever()
    
    
    • Jinja2模板
      虽然可以返回给用户HTML的内容以实现复杂的页面,但是如何返回动态的页面?

    • 自定义一套特殊的语法,进行替换

    • 使用开源工具jinja2,遵循其指定语法

    <!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>
    
    # encoding:utf-8
    __author__ = 'Fioman'
    __date__ = '2018/11/2 18:59'
    
    from wsgiref.simple_server import make_server
    from jinja2 import Template
    
    
    def index():
        with open('index.html')
            ret = f.read()
            template = Template(ret)
            data = template.render(name='John Doe', user_list=['alex', 'eric'])
    
            return data.encode('utf-8')
    
    
    def 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('127.0.0.1', 8000, run_server)
        print("Serving HTTP on port 8000....")
        httpd.serve_forever()
    

    总结一下

    web框架的本质

    socket服务端 和 浏览器客户端的通信

    socket服务端功能划分:

    • a. 负责和浏览器收发消息(socket通信) -> wsgiref/uWsgi/gunicorn...

    • b. 根据用户访问的不同的URL的路径,执行不同的处理函数

    • c. 从HTML读取内容,并且完成字符串的替换. -> Jinja2模板语言

    Web框架的分类

    • a. 框架自带a,b,c -> Tornado

    • b. 框架自带b和c,使用第三方的a -> Django

    • c. 框架自带b,使用第三方的a和c -> Flask

    相关文章

      网友评论

          本文标题:web框架的本质

          本文链接:https://www.haomeiwen.com/subject/bsdcxqtx.html