• web框架基础


    一、web框架本质

    我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。

    import socket
    
    sk = socket.socket()
    sk.bind(("127.0.0.1", 80))
    sk.listen()
    
    
    while True:
        conn, addr = sk.accept()
        data = conn.recv(8096)
        conn.send(b'hello world')
        conn.close()
    

    可以说Web服务本质上都是在这十几行代码基础上扩展出来的,用户在浏览器一输入网址,给服务端发送数据,那浏览器会发送什么数据?怎么发?这个谁来定? 所以,必须有一个统一的规则,让大家发送消息、接收消息的时候有个格式依据,不能随便写。

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

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

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

    import socket
    
    sk = socket.socket()
    sk.bind(("127.0.0.1", 80))
    sk.listen()
    
    
    while True:
        conn, addr = sk.accept()
        data = conn.recv(8096)
        print(data)
        conn.send(b'hello world')
        conn.close()
    

    输出结果:

    b'GET / HTTP/1.1
    
    Host: 127.0.0.1
    
    Connection: keep-alive
    Cache-Control: max-age=0
    
    Upgrade-Insecure-Requests: 1
    
    User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
    
    Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Encoding: gzip, deflate, br
    
    Accept-Language: zh-CN,zh;q=0.9
    
    Cookie: bdshare_firstime=1529473540203; csrftoken=WqST6GYvUUK9qKpkZlMnsbQzue1kOm5LknTvd9GQlr1MOj8yZ9YouYFjKLwgE9lb
    
    '
    

    我们发现收发的消息是要按照一定的格式来,这里就需要了解一下HTTP协议了

    HTTP协议(HyperText Transfer Protocol,超文本传输协议)是因特网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准。

    HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)

    HTTP协议定义Web客户端如何从Web服务器请求Web页面,以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文,请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行作为响应,响应的内容包括协议的版本、成功或者错误代码、服务器信息、响应头部和响应数据。然后客户端浏览器解析HTML内容

    HTTP请求方法:

    GET(向指定的资源发出“显示”请求。使用GET方法应该只用在读取数据,而不应当被用于产生“副作用”的操作中,例如在Web Application中。其中一个原因是GET可能会被网络蜘蛛等随意访问)

    HEAD(与GET方法一样,都是向服务器发出指定资源的请求。只不过服务器将不传回资源的本文部分。它的好处在于,使用这个方法可以在不必传输全部内容的情况下,就可以获取其中“关于该资源的信息”(元信息或称元数据))

    POST(向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件)。数据被包含在请求本文中。这个请求可能会创建新的资源或修改现有资源,或二者皆有)

    PUT(向指定资源位置上传其最新内容)

    DELETE(请求服务器删除Request-URI所标识的资源)

    TRACE(回显服务器收到的请求,主要用于测试或诊断)

    OPTIONS(这个方法可使服务器传回该资源所支持的所有HTTP请求方法。用'*'来代替资源名称,向Web服务器发送OPTIONS请求,可以测试服务器功能是否正常运作)

    CONNECT(HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。通常用于SSL加密服务器的链接(经由非加密的HTTP代理服务器))

    HTTP状态码:

    • 1xx消息——请求已被服务器接收,继续处理
    • 2xx成功——请求已成功被服务器接收、理解、并接受
    • 3xx重定向——需要后续操作才能完成这一请求
    • 4xx请求错误——请求含有词法错误或者无法被执行
    • 5xx服务器错误——服务器在处理某个正确请求时发生错误

    HTTP协议对收发消息的格式要求:

    HTTP 请求格式:

     

    HTTP响应格式:

    二、完善自定义的web框架

    import socket
    #index页面
    def f1(request):
        f = open('index.txt','rb')
        data = f.read()
        f.close()
        return data
    #home页面
    def f2(request):
        import pymysql
        # 使用pymsql连接数据库
        conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='', db='userbase')
        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('home.html','r',encoding='utf-8') template = f.read() f.close() # 模板渲染 data = template.replace('@@userlist@@',content) #替换掉原有文件里面的特殊符号内容 return bytes(data,encoding='utf-8') routers = [ ('/index', f1), ('/home.html', f2), ] def runserver(): sock = socket.socket() sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('127.0.0.1',80)) sock.listen(5) while True: conn,addr = sock.accept() 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(' ') 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__': runserver()

    三、wsgiref

    对于真实开发中的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开发环境用的就是这个模块来做服务器

    from wsgiref.simple_server import make_server
    def run_server(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ])  
       # 设置HTTP响应的状态码和头信息
        url = environ['PATH_INFO']  # 取到用户输入的url,可进行判断取舍
    
    if __name__ == '__main__':
        httpd = make_server('127.0.0.1', 8090, run_server)
        print("开始服务...")
        httpd.serve_forever()
    

    四、jinja2

    上面的自定义web框架中,我们使用字符串拼接后用特殊符号替换来完成html的渲染,其实模板渲染有个现成的工具: jinja2

    下载安装:pip install jinja2
    from jinja2 import Template
    def index():
        userlist=[{"id":1,"username":'jump',"age":12}]
        with open("index.html", "r") as f:
            data = f.read()
        template = Template(data)  # 生成模板文件
        ret = template.render(index=userlist)  # 把数据填充到模板里面
        return [bytes(ret, encoding="utf8"), ]
    
    
    #html页面部分
    {% for row in index %}
          <tr>
               <td>{{row.id}}</td>
               <td>{{row.username}}</td>
                <td>{{row.age}}</td>
          </tr>
    {% endfor %}
    

      

     补充:

    自定义web框架:

    1. socket服务端
    2. 根据URL不同返回不同的内容(路由系统:URL -> 函数)
    3. 结果返回给用户(模板引擎渲染:自己创造任意数据替换,jinja2)

    Web框架:
    根据上述3点web框架分类:
    - 1,2,3      --> Tornado
    - [第三方1],2,3 --> wsgiref -> Django
    - [第三方1],2,[第三方3] --> flask

    #!/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()

     

  • 相关阅读:
    python报错Enable tracemalloc to get the object allocation traceback
    解决pycharm每次新建项目都要重新安装一些第三方库的问题
    创建一个CA证书
    [转载]oracle 12C 《服务器、客户端安装》
    [转载]Windows Server 2016中添加AD域控制器
    [转载]Windows Server 2016中部署AD
    虚拟机 VMware Workstation Pro 15.5.0 及永久激活密钥
    Oracle给查询结果增加序列号
    创建自定义ssl证书用于https
    js:getAttribute
  • 原文地址:https://www.cnblogs.com/crazyjump/p/10396874.html
Copyright © 2020-2023  润新知