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\r\n
Host: 127.0.0.1\r\n
Connection: keep-alive\r\nCache-Control: max-age=0\r\n
Upgrade-Insecure-Requests: 1\r\n
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\r\n
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nAccept-Encoding: gzip, deflate, br\r\n
Accept-Language: zh-CN,zh;q=0.9\r\n
Cookie: bdshare_firstime=1529473540203; csrftoken=WqST6GYvUUK9qKpkZlMnsbQzue1kOm5LknTvd9GQlr1MOj8yZ9YouYFjKLwgE9lb\r\n\r\n'

我们发现收发的消息是要按照一定的格式来,这里就需要了解一下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('\r\n\r\n') temp_list = headers.split('\r\n') 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()

 

 

posted @ 2019-02-19 10:20  Crazyjump  阅读(425)  评论(0编辑  收藏  举报