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

    一、Web框架本质

    1. 自己开发Web框架
    	- socket
    	- http协议
    	- HTML知识
    	- 数据库(pymysql,SQLAlchemy)
    
    HTTP:
    	无状态、短连接
    	
    TCP:
    	不断开
    	
    WEB应用(网站):
    	Http协议:
    		发送:
    			POST /index HTTP/1.1
    			Host: 127.0.0.1:8080
    			Connection: keep-alive
    			Cache-Control: max-age=0
    			Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
    			User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.89 Safari/537.36
    			HTTPS: 1
    			Accept-Encoding: gzip, deflate, sdch
    			Accept-Language: zh-CN,zh;q=0.8
    			Cookie: csrftoken=hNmu2JOtntGMN0hSRSPmMQk2newEb3o8zb6pXW5Cc3m54IaA5VlTkUvqWsFezpni
    
    
    			p=123
    		
    		响应:
    			200 OK
    			Cache-Control:public, max-age=15
    			Connection:keep-alive
    			Content-Encoding:gzip
    			Content-Type:text/html; charset=utf-8
    			Date:Wed, 14 Jun 2017 01:21:17 GMT
    			Expires:Wed, 14 Jun 2017 01:21:33 GMT
    			Last-Modified:Wed, 14 Jun 2017 01:21:03 GMT
    			Transfer-Encoding:chunked
    			Vary:Accept-Encoding
    			X-Frame-Options:SAMEORIGIN
    			X-UA-Compatible:IE=10
    					
    			
    			
    			用户在页面看到的内容“字符串”(看到页面效果,由于浏览器解析)
    			
    
    	浏览器(socket客户端)
    		2. www.cnblogs.com(42.121.252.58,80)
    			sk.socket()
    			sk.connect((42.121.252.58,80))
    			
    			sk.send('我想要xx')
    		5. 接收
    		6. 连接断开
    		
    		
    		
    	博客园(socket服务端)
    		1. 监听ip和端口(42.121.252.58,80)
    			while True:
    				用户 = 等待用户连接
    				3. 收到'我想要xx'
    				4. 响应:“好”
    				用户断开
    	
    	
    
    	import socket
    
    	sock = socket.socket()
    	sock.bind(('127.0.0.1',8080))
    	sock.listen(5)
    
    	while True:
    		conn,addr = sock.accept() # hang住
    		# 有人来连接了
    		# 获取用户发送的数据
    		data = conn.recv(8096)
    		conn.send(b"HTTP/1.1 200 OK
    
    ")
    		conn.send(b'123123')
    		conn.close()
    
    	
    	
    	1. Http,无状态,短连接
    	2. 
    		浏览器(socket客户端)
    		网站(socket服务端)
    		
    	3. 自己写网站
    		a. socket服务端
    		b. 根据URL不同返回不同的内容
    			路由系统:
    				URL -> 函数
    		c. 字符串返回给用户
    			模板引擎渲染:
    				HTML充当模板(特殊字符)
    				自己创造任意数据
    			字符串
    			
    	4. Web框架:
    		框架种类:
    			- a,b,c					 --> Tornado
    			- [第三方a],b,c          --> wsgiref -> Django 
    			- [第三方a],b,[第三方c]  --> flask,
    			
    		分类:
    			- Django框架(Web。。。。。。)
    			- 其他
    

      

      

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

    #!/usr/bin/env python
    #coding:utf-8
      
    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()
    

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

    WSGI(Web Server Gateway Interface)是一种规范,它定义了使用python编写的web app与web server之间接口格式,实现web app与web server间的解耦。

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

    from wsgiref.simple_server import make_server
     
     
    def RunServer(environ, start_response):
        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()
    

      

     

    二、自定义Web框架

    1、框架

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

    #!/usr/bin/env python
    #coding:utf-8
    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 = 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, RunServer)
        print "Serving HTTP on port 8000..."
        httpd.serve_forever()
    

    2、模板引擎

    在上一步骤中,对于所有的login、index均返回给用户浏览器一个简单的字符串,在现实的Web请求中一般会返回一个复杂的符合HTML规则的字符串,所以我们一般将要返回给用户的HTML写在指定文件中,然后再返回。如:

    index.html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>Index</h1>
    
    </body>
    </html>
    

    login.html

    <!DOCTYPE html>
    <html>
    <head lang="en">
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <form>
            <input type="text" />
            <input type="text" />
            <input type="submit" />
        </form>
    </body>
    </html>
    
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    from wsgiref.simple_server import make_server
     
     
    def index():
        # return 'index'
        f = open('index.html')
        data = f.read()
        return data
     
     
    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()
    

    对于上述代码,虽然可以返回给用户HTML的内容以现实复杂的页面,但是还是存在问题:如何给用户返回动态内容?

    • 自定义一套特殊的语法,进行替换
    • 使用开源工具jinja2,遵循其指定语法

    index.html

    <!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>
    
    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
     
    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()
    

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

    补充:自己开发web框架

    一:实现静态网站

    import socket
    
    def f1(request):
        """
        处理用户请求,并返回相应的内容
        :param request: 用户请求的所有信息
        :return:
        """
        f = open('index.fsw','rb')
        data = f.read()
        f.close()
        return data
    
    def f2(request):
        f = open('aricle.tpl','r',encoding='utf-8')
        data = f.read()
        f.close()
        import time
        ctime = time.time()
        data = data.replace('@@sw@@',str(ctime))
        return bytes(data,encoding='utf-8')
    
    def f3(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        content_list = []
        for row in user_list:
            tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
            content_list.append(tp)
        content = "".join(content_list)
    
    
        f = open('userlist.html','r',encoding='utf-8')
        template = f.read()
        f.close()
    
        # 模板渲染(模板+数据)
        data = template.replace('@@sdfsdffd@@',content)
        return bytes(data,encoding='utf-8')
    
    def f4(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        f = open('hostlist.html','r',encoding='utf-8')
        data = f.read()
        f.close()
    
        # 基于第三方工具实现的模板渲染
        from jinja2 import Template
        template = Template(data)
        data = template.render(xxxxx=user_list,user='sdfsdfsdf')
        return data.encode('utf-8')
    
    
    routers = [
        ('/xxx', f1),
        ('/ooo', f2),
        ('/userlist.htm', f3),
        ('/host.html', f4),
    ]
    
    
    def run():
        sock = socket.socket()
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('127.0.0.1',8080))
        sock.listen(5)
    
        while True:
            conn,addr = sock.accept() # hang住
            # 有人来连接了
            # 获取用户发送的数据
            data = conn.recv(8096)
            data = str(data,encoding='utf-8')
            headers,bodys = data.split('
    
    ')
            temp_list = headers.split('
    ')
            method,url,protocal = temp_list[0].split(' ')
            conn.send(b"HTTP/1.1 200 OK
    
    ")
    
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
    
            if func_name:
                response = func_name(data)
            else:
                response = b"404"
    
            conn.send(response)
            conn.close()
    
    if __name__ == '__main__':
        run()
    s1.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>用户登录</h1>
        <form>
            <p><input type="text" placeholder="用户名" /></p>
            <p><input type="password" placeholder="密码" /></p>
        </form>
    </body>
    </html>
    index.fsw
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th>1</th>
                    <th>root</th>
                    <th>root@qq.com</th>
                </tr>
            </tbody>
        </table>
    </body>
    </html>
    aricle.tpl

    二:实现动态网站

    import socket
    
    def f1(request):
        """
        处理用户请求,并返回相应的内容
        :param request: 用户请求的所有信息
        :return:
        """
        f = open('index.fsw','rb')
        data = f.read()
        f.close()
        return data
    
    def f2(request):
        f = open('aricle.tpl','r',encoding='utf-8')
        data = f.read()
        f.close()
        import time
        ctime = time.time()
        data = data.replace('@@sw@@',str(ctime))
        return bytes(data,encoding='utf-8')
    
    def f3(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        content_list = []
        for row in user_list:
            tp = "<tr><td>%s</td><td>%s</td><td>%s</td></tr>" %(row['id'],row['username'],row['password'])
            content_list.append(tp)
        content = "".join(content_list)
    
    
        f = open('userlist.html','r',encoding='utf-8')
        template = f.read()
        f.close()
    
        # 模板渲染(模板+数据)
        data = template.replace('@@sdfsdffd@@',content)
        return bytes(data,encoding='utf-8')
    
    def f4(request):
        import pymysql
    
        # 创建连接
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='db666')
        cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
        cursor.execute("select id,username,password from userinfo")
        user_list = cursor.fetchall()
        cursor.close()
        conn.close()
    
        f = open('hostlist.html','r',encoding='utf-8')
        data = f.read()
        f.close()
    
        # 基于第三方工具实现的模板渲染
        from jinja2 import Template
        template = Template(data)
        data = template.render(xxxxx=user_list,user='sdfsdfsdf')
        return data.encode('utf-8')
    
    
    routers = [
        ('/xxx', f1),
        ('/ooo', f2),
        ('/userlist.htm', f3),
        ('/host.html', f4),
    ]
    
    
    def run():
        sock = socket.socket()
        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        sock.bind(('127.0.0.1',8080))
        sock.listen(5)
    
        while True:
            conn,addr = sock.accept() # hang住
            # 有人来连接了
            # 获取用户发送的数据
            data = conn.recv(8096)
            data = str(data,encoding='utf-8')
            headers,bodys = data.split('
    
    ')
            temp_list = headers.split('
    ')
            method,url,protocal = temp_list[0].split(' ')
            conn.send(b"HTTP/1.1 200 OK
    
    ")
    
            func_name = None
            for item in routers:
                if item[0] == url:
                    func_name = item[1]
                    break
    
            if func_name:
                response = func_name(data)
            else:
                response = b"404"
    
            conn.send(response)
            conn.close()
    
    if __name__ == '__main__':
        run()
    s1.py
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <h1>用户登录</h1>
        <form>
            <p><input type="text" placeholder="用户名" /></p>
            <p><input type="password" placeholder="密码" /></p>
        </form>
    </body>
    </html>
    index.fsw
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <th>1</th>
                    <th>@@sw@@</th>
                    <th>root@qq.com</th>
                </tr>
            </tbody>
        </table>
    </body>
    </html>
    aricle.tpl
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title></title>
    </head>
    <body>
        <table border="1">
            <thead>
                <tr>
                    <th>ID</th>
                    <th>用户名</th>
                    <th>邮箱</th>
                </tr>
            </thead>
            <tbody>
                {% for row in xxxxx %}
                    <tr>
                        <td>{{row.id}}</td>
                        <td>{{row.username}}</td>
                        <td>{{row.password}}</td>
                    </tr>
                {% endfor %}
            </tbody>
        </table>
        {{user}}
    </body>
    </html>
    userlist.html
  • 相关阅读:
    Java网络编程:OSI七层模型和TCP/IP模型介绍
    Java网络编程:IP地址和端口号
    Java缓冲流的优点和原理
    Java线程的优先级设置遵循什么原则?
    java笔试题大全带答案(经典11题)
    java笔试题大全之IO流常见选择题
    java笔试手写算法面试题大全含答案
    java笔试常见的选择题
    Java的类加载器都有哪些,每个类加载器都有加载那些类,什么是双亲委派模型,是做什么的?
    Java的安全性如何理解
  • 原文地址:https://www.cnblogs.com/xuyaping/p/7106213.html
Copyright © 2011-2022 走看看