Django
web框架的本质及自定义web框架
所有的Web应用本质上就是一个socket服务端,而用户的浏览器就是一个socket客户端,基于请求做出响应,客户都先请求,服务端做出对应的响应,按照http协议的请求协议发送请求,服务端按照http协议的响应协议来响应请求,这样的网络通信,我们就可以自己实现Web框架了。
基于socket来自己实现一个web框架,写一个web服务端,让浏览器来请求,并通过自己的服务端把页面返回给浏览器,浏览器渲染出我们想要的效果。
一、简单的web框架
import socket sk = socket.socket() sk.bind(('127.0.0.1',8001)) sk.listen() conn,addr = sk.accept() msg = conn.recv(1024) str_msg = msg.decode('utf-8') print(str_msg) conn.send(b'HTTP/1.1 200 ok \r\n\r\n') conn.send(b'hello world') conn.close() sk.close()
在浏览器访问服务端:
重启我们的代码,然后在网址中输入这个:
浏览器发过来一堆的消息,我们给浏览器回复(响应)信息的时候,也要按照一个消息格式来写,这些都是http协议规定的,那么我们就来学习一下http协议,然后继续完善我们的web框架:
HTTP协议:https://www.cnblogs.com/clschao/articles/9230431.html
二、返回Html文件的web框架:
首先写一个html文件,内容如下,名称为index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> h1{ background-color: green; color: white; } </style> </head> <body> <h1>世界,你好!</h1> <img src="https://img3.chouti.com/img_1557880036293.jpg?imageView2/1/q" alt=""> <script> alert('这是我们第一个网页') </script> </body> </html>
准备我们的python代码,服务端程序,文件内容如下,文件名称为 返回Html文件的web框架.py :
import socket sk = socket.socket() sk.bind(('127.0.0.1',8001)) sk.listen() conn,addr = sk.accept() msg = conn.recv(1024) str_msg = msg.decode('utf-8') print('浏览器请求信息:',str_msg) conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.html','rb') as f: data = f.read() conn.send(data) conn.close() sk.close()
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import socket from threading import Thread server = socket.socket() server.bind(('0.0.0.0',8001)) server.listen() def communication(conn): #接受请求数据 msg = conn.recv(1024).decode('utf-8') print(msg) #组合响应协议的消息格式,然后发送响应信息 conn.send(b'HTTP/1.1 200 ok \r\n\r\n') #打开index.html文件,返回给前端 with open('index.html', 'rb') as f: #没有引入文件形式的html页面 data = f.read() conn.send(data) conn.close() while 1: #接受连接 conn, add = server.accept() #开启线性并发 t = Thread(target=communication,args=(conn,)) t.start()
页面上输入网址看效果:css样式和js效果都有。
将index.html 文件中css样式与js语句写成文件,图片网络地址改为本地地址(01.jpg):
index.css:
h1{ background-color: green; color: white; }
index.js
alert('这是我们第一个网页')
index.html改为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="index.css"> <!--<style>--> <!--h1{--> <!--background-color: green;--> <!--color: white;--> <!--}--> <!--</style>--> </head> <body> <h1>世界,你好!</h1> <img src="01.jpg" alt=""> <!--<script>--> <!--alert('这是我们第一个网页')--> <!--</script>--> <script src="index.js"></script> </body> </html>
同样使用我们之前的python程序,来看效果:
需要将html页面需要的css、js、图片等文件也发送给浏览器就,并且这些静态文件都是浏览器单独过来请求的,其实和标签的属性有有关系,css文件是link标签的href属性:<link rel="stylesheet" href="test.css">,js文件是script标签的src属性:<script src="test.js"></script>,图片文件是img标签的src属性:<img src="meinv.png" alt="" width="100" height="100"> ,那个.ico文件是link标签的属性:<link rel="icon" href="wechat.ico">,其实这些属性都会在页面加载的时候,单独到自己对应的属性值里面取请求对应的文件数据,而且我们如果在值里面写的都是自己本地的路径,那么都会来自己的本地路径来找,如果我们写的是相对路径,就会到我们自己的网址+文件名称,这个路径来找它需要的文件,所以我们只需要将这些请求做一些响应,将对应的文件数据相应给浏览器就可以了!
三、返回静态文件的高级web框架
服务端程序:
import socket sk = socket.socket() sk.bind(('127.0.0.1',8001)) sk.listen() while 1: conn,addr = sk.accept() msg = conn.recv(1024) str_msg = msg.decode('utf-8') path = str_msg.split('\r\n')[0].split(' ')[1] conn.send(b'HTTP/1.1 200 ok \r\n\r\n') if path == '/': print(msg.decode('utf-8')) with open('index.html','rb') as f: data = f.read() conn.send(data) conn.close() elif path == '/01.jpg': with open('01.jpg','rb') as f: pic_data = f.read() conn.send(pic_data) conn.close() elif path == '/index.css': with open('index.css','rb') as f: css_data = f.read() conn.send(css_data) conn.close() elif path == '/wechat.ico': with open('wechat.ico','rb') as f: ico_data = f.read() conn.send(ico_data) conn.close() elif path == '/index.js': with open('index.js','rb') as f: js_data = f.read() conn.send(js_data) conn.close()
index.html:添加了<link rel="icon" href="wechat.ico">属性
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <link rel="stylesheet" href="index.css"> <link rel="icon" href="wechat.ico"> <!--<style>--> <!--h1{--> <!--background-color: green;--> <!--color: white;--> <!--}--> <!--</style>--> </head> <body> <h1>世界,你好!</h1> <img src="01.jpg" alt=""> <!--<script>--> <!--alert('这是我们第一个网页')--> <!--</script>--> <script src="index.js"></script> </body> </html>
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
import socket from threading import Thread server = socket.socket() server.bind(('0.0.0.0',8001)) server.listen() def communication(conn): msg = conn.recv(1024).decode('utf-8') print(msg) path = msg.split('\r\n')[0].split(' ')[1] print(path) #针对不同的请求路径,返回不同的文件 if path =='/': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('index.html', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path =='/index.css': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('index.css', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path =='/index.js': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('index.js', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path =='/wechat.ico': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('wechat.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() elif path =='/01.jpg': conn.send(b'HTTP/1.1 200 ok \r\nk1:v1\r\n\r\n') with open('01.jpg', 'rb') as f: data = f.read() conn.send(data) conn.close() while 1: conn, add = server.accept() t = Thread(target=communication,args=(conn,)) t.start()
四:函数多线程版返回静态文件的web框架
静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico
python代码:
import socket from threading import Thread server = socket.socket() server.bind(('127.0.0.1',8001)) server.listen() def html(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.html', 'rb') as f: data = f.read() conn.send(data) conn.close() def css(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.css','rb') as f: data = f.read() conn.send(data) conn.close() def js(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.js', 'rb') as f: data = f.read() conn.send(data) conn.close() def ico(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('wechat.ico','rb') as f: data = f.read() conn.send(data) conn.close() def jpg(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('01.jpg', 'rb') as f: data = f.read() conn.send(data) conn.close() urlpatterns = [ ('/',html), ('/index.css',css), ('/index.js',js), ('/wechat.ico',ico), ('/01.jpg',jpg), ] def communication(conn): msg = conn.recv(1024).decode('utf-8') print(msg) path = msg.split('\r\n')[0].split(' ')[1] print(path) #针对不同的请求路径,返回不同的文件 for urlpattern in urlpatterns: if urlpattern[0] == path: # urlpattern[1](conn) # 多线程执行函数 t = Thread(target=urlpattern[1],args=(conn,)) t.start() # if path =='/': # html(conn) # elif path =='/index.css': # css(conn) # elif path =='/index.js': # js(conn) # elif path =='/wechat.ico': # ico(conn) # elif path =='/01.jpg': # jpg(conn) while 1: conn, add = server.accept() t = Thread(target=communication,args=(conn,)) t.start()
五、返回不同html页面的web框架
静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico
添加返回的页面 home.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>首页</h1> <h1>time</h1> </body> </html>
python代码:
import socket from threading import Thread server = socket.socket() server.bind(('0.0.0.0',8001)) server.listen() def html(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.html', 'rb') as f: data = f.read() conn.send(data) conn.close() def css(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.css', 'rb') as f: data = f.read() conn.send(data) conn.close() def js(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.js', 'rb') as f: data = f.read() conn.send(data) conn.close() def ico(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('wechat.ico', 'rb') as f: data = f.read() conn.send(data) conn.close() def jpg(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('01.jpg', 'rb') as f: data = f.read() conn.send(data) conn.close() def home(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('home.html', 'rb') as f: data = f.read() conn.send(data) conn.close() urlpatterns = [ ('/',html), ('/index.css',css), ('/index.js',js), ('/wechat.ico',ico), ('/01.jpg',jpg), ('/home.html',home), ] def communication(conn): msg = conn.recv(1024).decode('utf-8') # print(msg) path = msg.split('\r\n')[0].split(' ')[1] #针对不同的请求路径,返回不同的文件 for urlpattern in urlpatterns: # print(path) if urlpattern[0] == path: # urlpattern[1](conn) # 多线程执行函数 t = Thread(target=urlpattern[1],args=(conn,)) t.start() while 1: conn, add = server.accept() t = Thread(target=communication,args=(conn,)) t.start()
六、返回动态l页面的web框架
静态文件使用:index.html , index.css , index.js , 01.jpg , wechat.ico ,home.html
python 代码:
import time import socket from threading import Thread server = socket.socket() server.bind(('0.0.0.0',8001)) server.listen() def html(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.html','rb') as f: data = f.read() conn.send(data) conn.close() def css(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.css','rb') as f: data = f.read() conn.send(data) conn.close() def js(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('index.js','rb') as f: data = f.read() conn.send(data) conn.close() def ico(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('wechat.ico','rb') as f: data = f.read() conn.send(data) conn.close() def jpg(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') with open('01.jpg', 'rb') as f: data = f.read() conn.send(data) conn.close() def home(conn): conn.send(b'HTTP/1.1 200 ok \r\n\r\n') current_time = str(time.time()) with open('home.html','r',encoding='utf-8') as f: data = f.read() data = data.replace('time',current_time) conn.send(data.encode('utf-8')) conn.close() urlpatterns = [ ('/',html), ('/index.css',css), ('/index.js',js), ('/favicon.ico',ico), ('/01.jpg',jpg), ('/home.html',home), ] def communication(conn): msg = conn.recv(1024).decode('utf-8') # print(msg) path = msg.split('\r\n')[0].split(' ')[1] #针对不同的请求路径,返回不同的文件 for urlpattern in urlpatterns: # print(path) if urlpattern[0] == path: # urlpattern[1](conn) # 多线程执行函数 t = Thread(target=urlpattern[1],args=(conn,)) t.start() while 1: conn, add = server.accept() t = Thread(target=communication,args=(conn,)) t.start()
七、wsgiref模块版web框架
wsgiref模块其实是将整个请求信息给封装了起来,不需要你自己处理了,假如它将所有请求信息封装成了一个叫做request的对象,那么你直接request.path就能获取到用户这次请求的路径,request.method就能获取到本次用户请求的请求方式(get还是post)等,那这个模块用起来,我们再写web框架就简单了很多。
对于真实开发中的python web程序来说,一般会分为两部分:服务器程序和应用程序。
服务器程序负责对socket服务器进行封装,并在请求到来时,对请求的各种数据进行整理。
应用程序负责具体的逻辑处理。为了方便应用程序的开发,就出现了众多的Web框架,例如:Django、Flask、web.py 等。不同的框架有不同的开发方式,但是无论如何,开发出的应用程序都要和服务器程序配合,才能为用户提供服务。
这样,服务器程序就需要为不同的框架提供不同的支持。这样混乱的局面无论对于服务器还是框架,都是不好的。对服务器来说,需要支持各种不同框架,对框架来说,只有支持它的服务器才能被开发出的应用使用。最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。如果要动态生成HTML,就需要把上述步骤自己来实现。不过,接受HTTP请求、解析HTTP请求、发送HTTP响应都是苦力活,如果我们自己来写这些底层代码,还没开始写动态HTML呢,就得花个把月去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。因为我们不希望接触到TCP连接、HTTP原始请求和响应格式,所以,需要一个统一的接口协议来实现这样的服务器软件,让我们专心用Python编写Web业务。
这时候,标准化就变得尤为重要。我们可以设立一个标准,只要服务器程序支持这个标准,框架也支持这个标准,那么他们就可以配合使用。一旦标准确定,双方各自实现。这样,服务器可以支持更多支持标准的框架,框架也可以使用更多支持标准的服务器。
WSGI(Web Server Gateway Interface)就是一种规范,它定义了使用Python编写的web应用程序与web服务器程序之间的接口格式,实现web应用程序与web服务器程序间的解耦。
常用的WSGI服务器有uwsgi、Gunicorn。而Python标准库提供的独立WSGI服务器叫wsgiref,Django开发环境用的就是这个模块来做服务器。
from wsgiref.simple_server import make_server # wsgiref本身就是个web框架,提供了一些固定的功能(请求和响应信息的封装,不需要我们自己写原生的socket了也不需要咱们自己来完成请求信息的提取了,提取起来很方便) #函数名字随便起 def application(environ, start_response): ''' :param environ: 是全部加工好的请求信息,加工成了一个字典,通过字典取值的方式就能拿到很多你想要拿到的信息 :param start_response: 帮你封装响应信息的(响应行和响应头),注意下面的参数 :return: ''' start_response('200 OK', [('Content-Type', 'text/html'),('k1','v1')]) print(environ) print(environ['PATH_INFO']) #输入地址127.0.0.1:8000,这个打印的是'/',输入的是127.0.0.1:8000/index,打印结果是'/index' return [b'<h1>Hello, web!</h1>'] httpd = make_server('127.0.0.1', 8080, application) print('Serving HTTP on port 8080...') # 开始监听HTTP请求: httpd.serve_forever()
简单wsgiref模块版web框架:
静态文件使用:index.html , home.html
import time from wsgiref.simple_server import make_server def app(environ,start_response): start_response('200 OK', [('Content-Type', 'text/html'), ('k1', 'v1')]) print(environ) path = environ['PATH_INFO'] for urlpattern in urlpatterns: print(path) if urlpattern[0] == path: ret = urlpattern[1]() return ret def html(): with open('index.html', 'rb') as f: data = f.read() return [data] def home(): current_time = str(time.time()) with open('home.html', 'r',encoding='utf-8') as f: data = f.read() data = data.replace('time',current_time).encode('utf-8') return [data] urlpatterns = [ ('/',html), ('/home.html',home), ] httpd = make_server('127.0.0.1', 8001, app) httpd.serve_forever()
一个完整的web项目,用户登录认证的项目,需要连接数据库,在mysql数据库里面准备一些表和数据:
mysql> create database day53; Query OK, 1 row affected (0.12 sec) mysql> use day53; Database changed mysql> create table userinfo(id int primary key auto_increment,username char(20) not null unique,password char(20) not null); Query OK, 0 rows affected (0.36 sec) mysql> insert into userinfo(username,password) values('xu','123'),('wu','456'); Query OK, 2 rows affected (0.17 sec) Records: 2 Duplicates: 0 Warnings: 0 mysql> select * from userinfo; +----+----------+----------+ | id | username | password | +----+----------+----------+ | 1 | xu | 123 | | 2 | wu | 456 | +----+----------+----------+ 2 rows in set (0.00 sec)
python服务端代码(主逻辑代码):
from urllib.parse import parse_qs from wsgiref.simple_server import make_server import webauth def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) # print(environ) # print(environ['PATH_INFO']) path = environ['PATH_INFO'] # 用户获取login页面的请求路径 if path == '/login': with open('login.html','rb') as f: data = f.read() # 针对form表单提交的auth路径,进行对应的逻辑处理 elif path == '/auth/': #登陆认证 #1.获取用户输入的用户名和密码 #2.去数据库做数据的校验,查看用户提交的是否合法 # user_information = environ[''] if environ.get("REQUEST_METHOD") == "POST": # 获取请求体数据的长度,因为提交过来的数据需要用它来提取,注意POST请求和GET请求的获取数据的方式不同 try: request_body_size = int(environ.get('CONTENT_LENGTH', 0)) except (ValueError): request_body_size = 0 # POST请求获取数据的方式 request_data = environ['wsgi.input'].read(request_body_size) print('>>>>>',request_data) # >>>>> b'username=chao&password=123',是个bytes类型数据 print('?????',environ['QUERY_STRING'])#????? 空的,因为post请求只能按照上面这种方式取数据 # parse_qs可以帮我们解析数据 re_data = parse_qs(request_data.decode('utf-8')) #将bytes类型数据转码 print('拆解后的数据',re_data) #拆解后的数据 {'username': ['wu'], 'password': ['456']} username = re_data['username'][0] password = re_data['password'][0] print(username,password) # 进行验证 status = webauth.auth(username,password) if status: # 3.将相应内容返回 with open('success.html','rb') as f: data = f.read() else: data = b'auth error' if environ.get("REQUEST_METHOD") == "GET": # GET请求获取数据的方式,只能按照这种方式取 print('?????',environ['QUERY_STRING']) #????? username=xu&password=123,是个字符串类型数据 request_data = environ['QUERY_STRING'] print('>>>>request_data', request_data) # parse_qs可以帮我们解析数据 re_data = parse_qs(request_data) print('拆解后的数据', re_data) username = re_data['username'][0] password = re_data['password'][0] print(username,password) # 进行验证 status = webauth.auth(username,password) if status: # 3.将相应内容返回 with open('success.html','rb') as f: data = f.read() else: data = b'auth error' # 不管是post还是get请求都不能直接拿到数据,拿到的数据还需要我们来进行分解提取,所以我们引入urllib模块来帮我们分解 else: data = b'sorry 404!,not found the page' return [data] httpd = make_server('127.0.0.1', 8001, application) print('Serving HTTP on port 8001...') # 开始监听HTTP请求: httpd.serve_forever()
登陆页面 login.html文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--form标签中 method属性可以等于get--> <form action="http://127.0.0.1:8001/auth/" method="post"> 用户名<input type="text" name="username"> 密码 <input type="password" name="password"> <input type="submit"> </form> </body> </html>
用户验证 webauth.py文件:
def auth(username,password): import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', database='day53', charset='utf8' ) print('userinfo',username,password) cursor = conn.cursor(pymysql.cursors.DictCursor) sql = 'select * from userinfo where username=%s and password=%s;' res = cursor.execute(sql, [username, password]) if res: return True else: return False
用户登陆成功跳转 success.html文件:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>恭喜登陆成功!</h1> </body> </html>
一个比较low的文件配置版web框架:
1.pycharm创建userinfo表格:
#创建表,插入数据 def createtable(): import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', database='day53', charset='utf8' ) cursor = conn.cursor(pymysql.cursors.DictCursor) sql = ''' -- 创建表 create table userinfo(id int primary key auto_increment,username char(20) not null unique,password char(20) not null); -- 插入数据 insert into userinfo(username,password) values('chao','666'),('sb1','222'); ''' cursor.execute(sql) conn.commit() cursor.close() conn.close()
2.框架主体:
from urls import urlpatterns from wsgiref.simple_server import make_server #这个文件里面是框架的主体内容 def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) path = environ['PATH_INFO'] for url_tuple in urlpatterns: if url_tuple[0] == path: data = url_tuple[1](environ) #environ要传进去,因为处理逻辑里面可能要用 break else: data = b'sorry 404!,not found the page' return [data] #注意昂,我们如果直接返回中文,没有给浏览器指定编码格式,默认是gbk,所以我们需要gbk来编码一下,浏览器才能识别 # data='登陆成功!'.encode('gbk') httpd = make_server('127.0.0.1', 8001, application) print('Serving HTTP on port 8001...') # 开始监听HTTP请求: httpd.serve_forever() #整个框架写好了,那么我们将来想添加一些新的功能,比如说有人想在网址里面输入http://127.0.0.1:8001/timer 来查看当前时间,你只需要两步,写一个url映射关系,写一个应对的处理函数就搞定了,有了框架就不需要你在重新写一遍这所有的逻辑了,简单两步搞定新功能
3.urls文件:
from views import login,auth,favicon,index,timer #url与视图函数的对应关系 urlpatterns=[ ('/login',login), ('/auth/',auth), ('/favicon.ico',favicon), ('/',index), ('/timer',timer), ]
4.views文件:
import datetime import webauth from urllib.parse import parse_qs def login(environ): with open('templates/login.html', 'rb') as f: data = f.read() return data def auth(environ): # 登陆认证 # 1.获取用户输入的用户名和密码 # 2.去数据库做数据的校验,查看用户提交的是否合法 # user_information = environ[''] if environ.get("REQUEST_METHOD") == "POST": # 获取请求体数据的长度,因为提交过来的数据需要用它来提取,注意POST请求和GET请求的获取数据的方式不同 try: request_body_size = int(environ.get('CONTENT_LENGTH', 0)) except (ValueError): request_body_size = 0 # POST请求获取数据的方式 request_data = environ['wsgi.input'].read(request_body_size) print('>>>>>', request_data) # >>>>> b'username=chao&password=123',是个bytes类型数据 print('?????', environ['QUERY_STRING']) # ????? 空的,因为post请求只能按照上面这种方式取数据 # parse_qs可以帮我们解析数据 re_data = parse_qs(request_data.decode('utf-8')) # 将bytes类型数据转码 print('拆解后的数据', re_data) # 拆解后的数据 {'username': ['wu'], 'password': ['456']} username = re_data['username'][0] password = re_data['password'][0] print(username, password) # 进行验证 status = webauth.auth(username, password) if status: # 3.将相应内容返回 with open('success.html', 'rb') as f: data = f.read() else: data = b'auth error' if environ.get("REQUEST_METHOD") == "GET": # GET请求获取数据的方式,只能按照这种方式取 print('?????', environ['QUERY_STRING']) # ????? username=chao&password=123,是个字符串类型数据 request_data = environ['QUERY_STRING'] # parse_qs可以帮我们解析数据 re_data = parse_qs(request_data) print('拆解后的数据', re_data) # 拆解后的数据 {'password': ['123'], 'username': ['chao']} username = re_data['username'][0] password = re_data['password'][0] print(username, password) # 进行验证: status = webauth.auth(username, password) if status: # 3.将相应内容返回 with open('templates/websuccess.html', 'rb') as f: data = f.read() else: data = b'auth error' return data def favicon(environ): with open('wechat.ico','rb') as f: data = f.read() return data def index(environ): with open('templates/index.html','rb') as f: data = f.read() return data #查看当前时间的 def timer(environ): data = str(datetime.datetime.now()).encode('utf-8') return data
5.用户名密码验证 webauth文件:
#对用户名和密码进行验证 def auth(username,password): import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', database='day53', charset='utf8' ) print('userinfo',username,password) cursor = conn.cursor(pymysql.cursors.DictCursor) sql = 'select * from userinfo where username=%s and password=%s;' res = cursor.execute(sql, [username, password]) if res: return True else: return False
6.templates 文件夹下html文件:
index.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <a href="http://127.0.0.1:8001/login">请登录</a> </body> </html>
login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <!--如果form表单里面的action什么值也没给,默认是往当前页面的url上提交你的数据,所以我们可以自己指定数据的提交路径--> <!--<form action="http://127.0.0.1:8001/auth/" method="post">--> <form action="http://127.0.0.1:8001/auth/" method="get"> 用户名<input type="text" name="username"> 密码 <input type="password" name="password"> <input type="submit"> </form> </body> </html>
websuccess.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> h1{ color:red; } </style> </head> <body> <h1>恭喜你登陆成功!</h1> </body> </html>
模板渲染JinJa2
上面的代码实现了一个简单的动态页面(字符串替换),我完全可以从数据库中查询数据,然后去替换我html中的对应内容(专业名词叫做模板渲染,你先渲染一下,再给浏览器进行渲染),然后再发送给浏览器完成渲染。 这个过程就相当于HTML模板渲染数据。 本质上就是HTML内容中利用一些特殊的符号来替换要展示的数据。 我这里用的特殊符号是我定义的,其实模板渲染有个现成的工具: jinja2
下载:在cmd命令提示符中输入或者pycharm中terminal
pip install jinja2
来一个html文件,index,html,内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>姓名:{{name}}</h1> <h1>爱好:</h1> <ul> {% for hobby in hobby_list %} <li>{{hobby}}</li> {% endfor %} </ul> </body> </html>
使用jinja2渲染index2.html文件,创建一个python文件:
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": "于谦", "hobby_list": ["抽烟", "喝酒","烫头"]}) # 把数据填充到模板里面
return [bytes(ret, encoding="utf8"), ]
# 定义一个url和函数的对应关系
URL_LIST = [
("/index", index),
]
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('', 8001, run_server)
print("Serving HTTP on port 8001...")
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的语法规则写上,其内部就会按照指定的语法进行相应的替换,从而达到动态的返回内容。