Web框架的本质

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

Python中使用socket和浏览器进行通信

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"OK")
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"OK")
conn.close()

浏览器向socket服务器发送的请求输出:

b'GET / HTTP/1.1\r\nHost: 127.0.0.1:8080\r\nConnection: keep-alive\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8\r\nDNT: 1\r\nAccept-Encoding: gzip, deflate, br\r\nAccept-Language: zh-CN,zh;q=0.9\r\nCookie: csrftoken=RKBXh1d3M97iz03Rpbojx1bR6mhHudhyX5PszUxxG3bOEwh1lxFpGOgWN93ZH3zv\r\n\r\n'

然后我们再看一下我们访问博客园官网时浏览器收到的响应信息是什么。

响应相关信息可以在浏览器调试窗口的network标签页中看到。

点击view source之后显示如下图:

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

HTTP协议介绍

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

每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type表明响应的内容格式。如 text/html表示HTML网页。

HTTP中请求格式:

HTTP中响应格式:

支持HTTP的socket简单web服务器:

import socket

server = socket.socket()

server.bind(("127.0.0.1",8080))
server.listen()
while 1:
conn,_ = server.accept()
data = conn.recv(8096)
# print(data) conn.send(b'HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf-8\r\n\r\n')
conn.send(b"Hello World")
# conn.send(b'<h1>简单web服务器测试</h1>') 这行会报错,因为 中文超出了ascii范围
conn.send(b'<h1>web</h1>')
conn.send(b'<h1>hello s10!</h1>')
conn.close()

根据不同的URL返回不同的内容:

import socket

'''
根据不同的url ,返回不同的网页
http:127.0.0.1/index/ 请求服务器的时候
GET /index/ HTTP/1.1\r\n --> 浏览器会请求行中会 /index/ 参数
'''
server = socket.socket() server.bind(("127.0.0.1",8080))
server.listen()
while 1:
conn,_ = server.accept()
data = conn.recv(8096)
print(data)
request_list = str(data, encoding="utf-8").split()
print(request_list[1])
conn.send(b'HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf-8\r\n\r\n')
if request_list[1] == "/index/":
response = b"<h1>index</h1>"
elif request_list[1] == '/home/':
response = b'<h1>home</h1>'
else:
response = b'404 Not Found!' conn.send(response)
conn.close()

再优化,利用函数:

import socket

'''
根据 客户端请求的url 与 函数进行映射
不同的url调用不同的业务处理函数
''' def index(item):
return "<h1>" + str(item).strip('/') + "</h1>" def home(item):
return "<h1>" + str(item).strip('/') + "</h1>" def F404(item):
return "<h1>" + str(item).strip('/') + ' Not Found!!!' + "</h1>" server = socket.socket() server.bind(("127.0.0.1",8080))
server.listen() FUNC = [
('/index/',index),
('/home/',home),
]
fun = None
while 1:
conn,_ = server.accept()
data = conn.recv(8096)
request_list = str(data, encoding="utf-8").split()
conn.send(b'HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf-8\r\n\r\n') for i in FUNC:
if request_list[1] == i[0]:
fun = i[1]
break
else:
fun = F404 response = fun(request_list[1])
conn.send(bytes(response,encoding="utf-8"))
conn.close()

再优化,返回一个HTML网页给客户端浏览器:

import socket

'''
根据 客户端请求的url 与 函数进行映射
不同的url调用不同的业务处理函数
然后将对应的html文件读取并加载之内存返回给客户端
''' def index(item):
with open("index.html","rb") as f:
ret = f.read()
return ret def home(item):
with open("home.html", "rb") as f:
ret = f.read()
return ret def F404(item):
return bytes("<h1>" + str(item).strip('/') + ' Not Found!!!' + "</h1>",encoding="utf-8") server = socket.socket() server.bind(("127.0.0.1",8080))
server.listen() FUNC = [
('/index/',index),
('/home/',home),
]
fun = None
while 1:
conn,_ = server.accept()
data = conn.recv(8096)
request_list = str(data, encoding="utf-8").split()
conn.send(b'HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf-8\r\n\r\n') for i in FUNC:
if request_list[1] == i[0]:
fun = i[1]
break
else:
fun = F404 response = fun(request_list[1])
conn.send(response)
conn.close()

返回一个动态网页:

import socket

'''
根据 客户端请求的url 与 函数进行映射
不同的url调用不同的业务处理函数
'''
import time
def index(item):
with open("dynamic.html",mode="r",encoding="utf-8") as f:
ret = f.read()
ret = ret.replace("{{time}}",str(time.time()))
return bytes(ret,encoding="utf-8") def home(item):
with open("home.html", "rb") as f:
ret = f.read()
return ret def F404(item):
return bytes("<h1>" + str(item).strip('/') + ' Not Found!!!' + "</h1>",encoding="utf-8") server = socket.socket() server.bind(("127.0.0.1",8080))
server.listen() FUNC = [
('/index/',index),
('/home/',home),
]
fun = None
while 1:
conn,_ = server.accept()
data = conn.recv(8096)
request_list = str(data, encoding="utf-8").split()
conn.send(b'HTTP/1.1 200 OK\r\nContent-Type:text/html;charset=utf-8\r\n\r\n') for i in FUNC:
if request_list[1] == i[0]:
fun = i[1]
break
else:
fun = F404 response = fun(request_list[1])
conn.send(response)
conn.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案例:

"""
根据URL中不同的路径返回不同的内容--函数进阶版
返回HTML页面
让网页动态起来
wsgiref模块版
""" import time
from wsgiref.simple_server import make_server # 将返回不同的内容部分封装成函数
def index(url):
with open("index.html", "r", encoding="utf8") as f:
s = f.read()
now = str(time.time())
s = s.replace("@@oo@@", now)
return bytes(s, encoding="utf8") def home(url):
with open("home.html", "r", encoding="utf8") as f:
s = f.read()
return bytes(s, encoding="utf8") # 定义一个url和实际要执行的函数的对应关系
list1 = [
("/index/", index),
("/home/", home),
] def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
func = None
for i in list1:
if i[0] == url:
func = i[1]
break
if func:
response = func(url)
else:
response = b"404 not found!"
# return [response, ]
return [b"<h1>Hello World</h1>",] # 返回一个列表 if __name__ == '__main__':
httpd = make_server('127.0.0.1', 8090, run_server)
print("我在8090等你哦...")
httpd.serve_forever()

WSGI代码

jinja2案例:

from wsgiref.simple_server import make_server
from jinja2 import Template def index():
with open("index.html", "r",encoding="utf-8") as f:
data = f.read()
template = Template(data) # 生成模板文件
ret = template.render({"name": "Alex", "hobby_list": ["烫头", "泡吧"]}) # 把数据填充到模板里面
return [bytes(ret, encoding="utf8"), ] def home():
with open("home.html", "rb") as f:
data = f.read()
return [data, ] # 定义一个url和函数的对应关系
URL_LIST = [
("/index/", index),
("/home/", home),
] def run_server(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html;charset=utf8'), ]) # 设置HTTP响应的状态码和头信息
url = environ['PATH_INFO'] # 取到用户输入的url
func = None # 将要执行的函数
for i in URL_LIST:
if i[0] == url:
func = i[1] # 去之前定义好的url列表里找url应该执行的函数
break
if func: # 如果能找到要执行的函数
return func() # 返回函数的执行结果
else:
return [bytes("404没有该页面", encoding="utf8"), ] if __name__ == '__main__':
httpd = make_server('', 8000, run_server)
print("Serving HTTP on port 8000...")
httpd.serve_forever()

jinja2d代码

Python-Web框架的本质的更多相关文章

  1. python django基础一web框架的本质

    web框架的本质就是一个socket服务端,而浏览器就是一个socker客户端,基于请求做出相应,客户端先请求,服务器做出对应响应 按照http协议的请求发送,服务器按照http协议来相应,这样的通信 ...

  2. Python Web框架本质——Python Web开发系列一

    前言:了解一件事情本质的那一瞬间总能让我获得巨大的愉悦感,希望这篇文章也能帮助到您. 目的:本文主要简单介绍Web开发中三大基本功能:Socket实现.路由系统.模板引擎渲染. 进入正题. 一. 基础 ...

  3. 浅谈网站web框架的本质

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

  4. python web框架之Tornado

    说Tornado之前分享几个前端不错的网站: -- Bootstrap http://www.bootcss.com/ -- Font Awesome http://fontawesome.io/ - ...

  5. 一步一步理解 python web 框架,才不会从入门到放弃

    要想清楚地理解 python web 框架,首先要清楚浏览器访问服务器的过程. 用户通过浏览器浏览网站的过程: 用户浏览器(socket客户端) 3. 客户端往服务端发消息 6. 客户端接收消息 7. ...

  6. web框架的本质

    一 web框架的本质及自定义web框架 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响 ...

  7. web 框架的本质及自定义web框架 模板渲染jinja2 mvc 和 mtv框架 Django框架的下载安装 基于Django实现的一个简单示例

    Django基础一之web框架的本质 本节目录 一 web框架的本质及自定义web框架 二 模板渲染JinJa2 三 MVC和MTV框架 四 Django的下载安装 五 基于Django实现的一个简单 ...

  8. 自定义python web框架

    -- Bootstrap http://www.bootcss.com/ -- Font Awesome http://fontawesome.io/ -- bxslider http://bxsli ...

  9. django基础一之web框架的本质

    一 web框架的本质及自定义web框架 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响 ...

  10. day 52 Django基础一之web框架的本质

    Django基础一之web框架的本质   django第一天 本节目录 一 web框架的本质及自定义web框架 二 模板渲染JinJa2 三 MVC和MTV框架 四 Django的下载安装 五 基于D ...

随机推荐

  1. Swift使用SDWebImage处理远程图片资源

    第一步:配置SDWebImage 打开github,https://github.com/rs/SDWebImage,将SDWebImage下载到本地 用Xcode创建一个swift的singleVi ...

  2. 【NOI2016】区间

    目链接:http://uoj.ac/problem/222 在数轴上有 n 个闭区间 [l1,r1],[l2,r2],...,[ln,rn].现在要从中选出 m 个区间,使得这 m 个区间共同包含至少 ...

  3. 【最小割】【网络流24题】【P2762】 太空飞行计划问题

    Description W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,-,Em},和进行这些实验需要使 ...

  4. 【题解】【THUSC 2016】成绩单 LOJ 2292 区间dp

    Prelude 快THUWC了,所以补一下以前的题. 真的是一道神题啊,网上的题解没几篇,而且还都看不懂,我做了一天才做出来. 传送到LOJ:(>人<:) Solution 直接切入正题. ...

  5. Kubernetes Downward API

    目录 说明 环境变量方式 将pod信息注入为环境变量 将容器资源信息注入为环境变量 volume挂载方式 作用 说明 我们知道,每个Pod在成功创建出来之后,都会被系统分配唯一的名字.IP地址,并且处 ...

  6. Hadoop生态圈-hive优化手段-作业和查询优化

    Hadoop生态圈-hive优化手段-作业和查询优化 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.

  7. IOS使用mkdir创建目录

    在IOS真机上可以创建目录的位置只有两个Documents和Caches,如果直接在NSHomeDirectory()上创建目录,会失败,返回的errno含义为操作被禁止. 获取Caches中的一个目 ...

  8. ArcGis数据分类(ClassBreaksDefinition)

    文章转载自:Arcgis数据分类那些事儿 数据分类.地图分级(Classification)通常用于Choropleth Map(面量图)的制作中,是专题制图中最初,也是最基本的一个步骤.没有进行过分 ...

  9. Android 实现两个list分别出现(在某一时刻只出现一个控件)

    第一种方法: 在.xml文件中将这两个List分别放入不同的布局管理器中,比如说 <RelativeLayout android:layout_width="match_parent& ...

  10. java后台代码发送邮件

    1:安装  eyoumailserversetup 易邮邮件服务器 注册账号 2:安装Foxmail 登录以后会有个还原页面 3:测试 4:java 代码编写 配置文件: mail.host=http ...