Django框架----Web框架本质
Web框架本质
我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端。 这样我们就可以自己实现Web框架了。
半成品自定义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"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()
输出:
- 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标签页中看到。
我们发现收发的消息需要按照一定的格式来,这里就需要了解一下HTTP协议了。
HTTP协议对收发消息的格式要求
每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的。 HTTP响应的Header中有一个 Content-Type
表明响应的内容格式。如 text/html
表示HTML网页。
HTTP GET请求的格式:
- GET /path HTTP/1.1
- header1:v1\r\n
- header2:v2\r\n
使用 \r\n
分隔多个header
HTTP POST请求格式:
- POST /path HTTP/1.1
- header1:v1\r\n
- header2:v2\r\n
- \r\n\r\n
- 请求体...
当遇到连续两个 \r\n\r\n
时,表示Header部分结束了,后面的数据是Body。
HTTP响应的格式:
- 200 OK
- Header1:v1\r\n
- Header2:v2\r\n
- \r\n\r\n
- 响应体...
处女版自定义web框架
经过上面的补充学习,我们知道了要想让我们自己写的web server端正经起来,必须要让我们的Web server在给客户端回复消息的时候按照HTTP协议的规则加上响应状态行,这样我们就实现了一个正经的Web框架了。
- import socket
- sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- sock.bind(('127.0.0.1', 8000))
- sock.listen()
- while True:
- conn, addr = sock.accept()
- data = conn.recv(8096)
- # 给回复的消息加上响应状态行
- conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
- conn.send(b"OK")
- conn.close()
我们通过十几行代码简单地演示了web 框架的本质。
接下来就让我们继续完善我们的自定义web框架吧!
根据不同的路径返回不同的内容
这样就结束了吗? 如何让我们的Web服务根据用户请求的URL不同而返回不同的内容呢?
小事一桩,我们可以从请求相关数据里面拿到请求URL的路径,然后拿路径做一个判断...
- """
- 根据URL中不同的路径返回不同的内容
- """
- import socket
- sk = socket.socket()
- sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
- sk.listen() # 监听
- while 1:
- # 等待连接
- conn, add = sk.accept()
- data = conn.recv(8096) # 接收客户端发来的消息
- # 从data中取到路径
- data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
- # 按\r\n分割
- data1 = data.split("\r\n")[0]
- url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
- conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
- # 根据不同的路径返回不同内容
- if url == "/index/":
- response = b"index"
- elif url == "/home/":
- response = b"home"
- else:
- response = b"404 not found!"
- conn.send(response)
- conn.close()
根据不同的路径返回不同的内容--函数版
上面的代码解决了不同URL路径返回不同内容的需求。
但是问题又来了,如果有很多很多路径要判断怎么办?难道要挨个写if判断? 当然不用,我们有更聪明的办法。
- """
- 根据URL中不同的路径返回不同的内容--函数版
- """
- import socket
- sk = socket.socket()
- sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
- sk.listen() # 监听
- # 将返回不同的内容部分封装成函数
- def index(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding="utf8")
- def home(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding="utf8")
- while 1:
- # 等待连接
- conn, add = sk.accept()
- data = conn.recv(8096) # 接收客户端发来的消息
- # 从data中取到路径
- data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
- # 按\r\n分割
- data1 = data.split("\r\n")[0]
- url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
- conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
- # 根据不同的路径返回不同内容,response是具体的响应体
- if url == "/index/":
- response = index(url)
- elif url == "/home/":
- response = home(url)
- else:
- response = b"404 not found!"
- conn.send(response)
- conn.close()
根据不同的路径返回不同的内容--函数进阶版
看起来上面的代码还是要挨个写if判断,怎么办?我们还是有办法!(只要思想不滑坡,方法总比问题多!)
- """
- 根据URL中不同的路径返回不同的内容--函数进阶版
- """
- import socket
- sk = socket.socket()
- sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
- sk.listen() # 监听
- # 将返回不同的内容部分封装成函数
- def index(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding="utf8")
- def home(url):
- s = "这是{}页面!".format(url)
- return bytes(s, encoding="utf8")
- # 定义一个url和实际要执行的函数的对应关系
- list1 = [
- ("/index/", index),
- ("/home/", home),
- ]
- while 1:
- # 等待连接
- conn, add = sk.accept()
- data = conn.recv(8096) # 接收客户端发来的消息
- # 从data中取到路径
- data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
- # 按\r\n分割
- data1 = data.split("\r\n")[0]
- url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
- conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
- # 根据不同的路径返回不同内容
- 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!"
- # 返回具体的响应消息
- conn.send(response)
- conn.close()
返回具体的HTML文件
完美解决了不同URL返回不同内容的问题。 但是我不想仅仅返回几个字符串,我想给浏览器返回完整的HTML内容,这又该怎么办呢?
没问题,不管是什么内容,最后都是转换成字节数据发送出去的。 我们可以打开HTML文件,读取出它内部的二进制数据,然后再发送给浏览器。
- """
- 根据URL中不同的路径返回不同的内容--函数进阶版
- 返回独立的HTML页面
- """
- import socket
- sk = socket.socket()
- sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
- sk.listen() # 监听
- # 将返回不同的内容部分封装成函数
- def index(url):
- # 读取index.html页面的内容
- with open("index.html", "r", encoding="utf8") as f:
- s = f.read()
- # 返回字节数据
- 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),
- ]
- while 1:
- # 等待连接
- conn, add = sk.accept()
- data = conn.recv(8096) # 接收客户端发来的消息
- # 从data中取到路径
- data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
- # 按\r\n分割
- data1 = data.split("\r\n")[0]
- url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
- conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
- # 根据不同的路径返回不同内容
- 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!"
- # 返回具体的响应消息
- conn.send(response)
- conn.close()
让网页动态起来
这网页能够显示出来了,但是都是静态的啊。页面的内容都不会变化的,我想要的是动态网站。
没问题,我也有办法解决。我选择使用字符串替换来实现这个需求。(这里使用时间戳来模拟动态的数据)
- """
- 根据URL中不同的路径返回不同的内容--函数进阶版
- 返回HTML页面
- 让网页动态起来
- """
- import socket
- import time
- sk = socket.socket()
- sk.bind(("127.0.0.1", 8080)) # 绑定IP和端口
- sk.listen() # 监听
- # 将返回不同的内容部分封装成函数
- 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),
- ]
- while 1:
- # 等待连接
- conn, add = sk.accept()
- data = conn.recv(8096) # 接收客户端发来的消息
- # 从data中取到路径
- data = str(data, encoding="utf8") # 把收到的字节类型的数据转换成字符串
- # 按\r\n分割
- data1 = data.split("\r\n")[0]
- url = data1.split()[1] # url是我们从浏览器发过来的消息中分离出的访问路径
- conn.send(b'HTTP/1.1 200 OK\r\n\r\n') # 因为要遵循HTTP协议,所以回复的消息也要加状态行
- # 根据不同的路径返回不同内容
- 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!"
- # 返回具体的响应消息
- 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
我们利用wsgiref模块来替换我们自己写的web框架的socket server部分:
- """
- 根据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, ]
- if __name__ == '__main__':
- httpd = make_server('127.0.0.1', 8090, run_server)
- print("我在8090等你哦...")
- httpd.serve_forever()
jinja2
上面的代码实现了一个简单的动态,我完全可以从数据库中查询数据,然后去替换我html中的对应内容,然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2
下载jinja2:
- pip3 install jinja2
- <!DOCTYPE html>
- <html lang="zh-CN">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="x-ua-compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <title>Title</title>
- </head>
- <body>
- <h1>姓名:{{name}}</h1>
- <h1>爱好:</h1>
- <ul>
- {% for hobby in hobby_list %}
- <li>{{hobby}}</li>
- {% endfor %}
- </ul>
- </body>
- </html>
index2.html文件
使用jinja2渲染index2.html文件:
- from wsgiref.simple_server import make_server
- from jinja2 import Template
- def index():
- with open("index2.html", "r") 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()
现在的数据是我们自己手写的,那可不可以从数据库中查询数据,来填充页面呢?
使用pymysql连接数据库:
- conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="xxx", db="xxx", charset="utf8")
- cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
- cursor.execute("select name, age, department_id from userinfo")
- user_list = cursor.fetchall()
- cursor.close()
- conn.close()
创建一个测试的user表:
- CREATE TABLE user(
- id int auto_increment PRIMARY KEY,
- name CHAR(10) NOT NULL,
- hobby CHAR(20) NOT NULL
- )engine=innodb DEFAULT charset=UTF8;
模板的原理就是字符串替换,我们只要在HTML页面中遵循jinja2的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。
本文截取自哪吒语录,原博客地址:http://www.cnblogs.com/liwenzhou/p/8258992.html
Django框架----Web框架本质的更多相关文章
- Web框架本质及第一个Django实例 Web框架
Web框架本质及第一个Django实例 Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web ...
- Django学习---Web框架及基础知识
Django学习---Web框架 web框架的本质 我们在学socket,我们创建一个socketserver,然后运行起来,有一个client客户端要连接socket服务端,连接上之后,如果两边都没 ...
- Django之web框架和url路由
一.web框架 1.什么是web框架 Web框架是用来进行Web应用开发的一个软件架构,开发者在基于Web框架实现自己的业务逻辑.Web应用本质上就是一个socket服务端,而用户的浏览器就是一个so ...
- Django之Web框架本质及第一个Django实例
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. 半成品自定义web框架 impor ...
- Django 基础 web框架本质
Web框架本质 我们可以这样理解:所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端. 这样我们就可以自己实现Web框架了. import socket sk ...
- 【Django】Web框架本质
目录 根据不同的路径返回不同的内容 普通版 函数版 函数进阶版 返回具体的HTML文件 让网页动态起来 服务器和应用程序 wsgiref 模块 @ * 我们可以这样理解:所有的==Web应用本质上就是 ...
- Django: 之Web框架完美解析
Web框架解析 Web通过Socket来监听客户端,,一旦发现客户发送的信息立刻接受.接受之后在服务端查找客户的请求,找到请求返回给用户,断开.这是一个连接,不断的接收,不断的返回. #!/usr/b ...
- 框架----Web框架本质
一.Web框架本质 众所周知,对于所有的Web应用,本质上其实就是一个socket服务端,用户的浏览器其实就是一个socket客户端. #!/usr/bin/env python #coding:ut ...
- django基础(web框架,http协议,django安装)
学习Django之前我们先来看什么是OSI七层模型: 应用层 表示层 应用层(五层模型中把这三层合成一个应用层) http协议 会话层 传输层 提供端口对 ...
随机推荐
- ajax跨域获取返回值
js代码 $.ajax({ async:false, url: 'https://***/api/prepareApi.getDanMu?sqlMapId=findBarrage', // 跨域URL ...
- linux使用bin文件安装jdk
jdk1.6.20文件为bin文件安装过程如下 添加执行权限 chmod +x jdk-6u20-linux-x64.bin 运行,出现提示需要输入yes ./jdk-6u20-linux-x64.b ...
- ubuntu16.04下安装kdevelop和汉化
1.Kdevelop安装 最简单的命令行安装,打开终端,执行 sudo apt-get install kdevelop 2.Kdevelop汉化 不需要自己下载汉化包,Kdevelop安装后,在终端 ...
- python中super的使用方法
说白了,super的使用就是要子类要调用父类的方法,我们就用super,那你要有调用的规范,我们明白这个规范就可以了. 在python2和python3中,调用方法不同,注意就是了.Python3.x ...
- Vue中父子组件执行的先后顺序探讨
前几天,朋友向我提出了一个关于Vue中父子组件执行的先后顺序问题,相信很多朋友在学习的过程中也会遇到这个问题,所以我就在此提出我自己的一些小看法. 问题如下:请问下图中父子组件执行的先后顺序? 首先, ...
- Shiro的使用
前言 相比有做过企业级开发的童鞋应该都有做过权限安全之类的功能吧,最先开始我采用的是建用户表,角色表,权限表,之后在拦截器中对每一个请求进行拦截,再到数据库中进行查询看当前用户是否有该权限,这样的设计 ...
- 最强Mac电脑 工作站级别一体机iMac Pro公布
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/a2Ni5KFDaIO1E6/article/details/78795857 前不久传出消息,苹果将 ...
- UITextField输入中文
[self.rightTF addTarget:self action:@selector(rightTFValueChange:) forControlEvents:UIControlEventEd ...
- Spark性能优化(二)
资源调优 调优概述 在开发完Spark作业之后,就该为作业配置合适的资源了.Spark的资源参数,基本都可以在spark-submit命令中作为参数设置.很多Spark初学者,通常不知道该设置哪些必要 ...
- 用Servlet获取表单数据
用Servlet获取表单数据 在webroot下新建userRegist2.jsp 代码如下: <%@ page contentType="text/html;charset=gb23 ...