搭建一个简单的Web框架
概述
本文通过使用 wsgiref 模块搭建一个简单的WEB框架,旨在了解框架的基本运行原理
基本的接受数据跟发送数据
from wsgiref.simple_server import make_server def application(environ, start_response): print(environ) # 客户端返回给服务器的内容已经被打包好放在了这个environ字典里面了 start_response('200 OK', [('Content-Type', 'text/html')]) return [b'<h1>Hello, Web!</h1>'] httpd = make_server('', 8080, application) print('Serving HTTP ON PORT 8080...') # 开始监听HTTP请求 httpd.serve_forever() # ① """ python内置了WSGI服务器wsgiref相当于一个服务软件,作用与apache和nginx等相似,设置好了HTTP请求 程序启动后会在①处阻塞住等待客户端(浏览器)的请求,当有请求后会执行application方法, 在application方法中,存放所有的业务逻辑 environ是一个字典,将所有的来自客户端的HTTP请求信息封装成一个字典,需要什么直接去取就可以了, start_response()是一个发送http响应的函数,执行该函数一次,就是发送了HTTP响应的Header,Header只能发送一次, 也就是说该方法只能调用一次,方法接收两个参数,一个是HTTP响应码,一个是一组list表示HTTP的Header,每一个Header用 一个包含两个字符串(键跟值)的元组表示 在方法的最后会返回一个字符串列表,该内容就是HTTP响应的Body部分,如果太长可以通过文件读取的方式返回,注意,必须是字节类型!! """
通过URL跳转到对应的页面
上面通过wsgiref模块总算是能够跟浏览器"说上话了",但是如何做到输入不同的URL来跳转到不同的页面呢,其实只需要获取 environ 字典中的 "PATH_INFO",这里面就存有这次http请求的URL,我们只需要拿到该URL,然后根据不同的URL返回不听的页面即可
# 业务升级,通过URL实现跳转到相关的页面 from wsgiref.simple_server import make_server # 处理URL为 /xin 的请求 def foo1(): f = open("index1.html") data = f.read() f.close() return data # 处理URL为 /scarlett 的请求 def foo2(): f = f = open("index2,html") data = f.read() f.close() return data def application(environ, start_response): print(environ["PATH_INFO"]) # 该键对应的值就是服务器名后面的路径,比如127.0.0.1:8080/xin,拿到的就是/xin start_response('200 OK', [('Content-Type', 'text/html')]) path = environ["PATH_INFO"] # 获取这次http请求的URL # 根据URL判断返回的页面 if path == "/xin": return [foo1()] elif path == "/scarlett": return [foo2()] else: return [b'<h1>Hello, Web!</h1>'] httpd = make_server('', 8080, application) print('Serving HTTP ON PORT 8080...') # 开始监听HTTP请求 httpd.serve_forever() # 等待浏览器返回数据
通过一个单独的函数处理URL
上面已经解决了根据不同的URL跳转到不同的页面,但是是通过 if elif 去判断每个URL的,这样写的弊端算是很明显的,就是如果这里有100个不同的URL,岂不是要写100个 if elif?这样做的效率显然是很低的,那么该怎样解决呢?
这里提出了一种办法,就是专门拿出一个函数去处理URL请求,也就是路由映射
from wsgiref.simple_server import make_server def login(req): pass def register(req): pass # 路由函数,解决URL过多问题 # 在这里面定义一个列表,值由是一个个元组,将URL路径对相应的处理函数对应了起来 def router(): urlpatterns = [ ('/login', login), ('/register', register), ] return urlpatterns def application(environ, start_response): print(environ["PATH_INFO"]) # 该键对应的值就是服务器名后面的路径,比如127.0.0.1:8080/xin,拿到的就是/xin start_response('200 OK', [('Content-Type', 'text/html')]) path = environ["PATH_INFO"] # 获取到这次http请求的URL路径 urlpattern = router() # 循环遍历里面的参数跟PATH_INFO匹配 func = None for item in urlpattern: if item[0] == path: func = item[1] break # 找到后跳出,不在循环后面的内容 return [func(environ)] # 将客户端HTTP请求信息传进去 httpd = make_server('', 8080, application) print('Serving HTTP ON PORT 8080...') # 开始监听HTTP请求 httpd.serve_forever()
在前端显示后端传入的变量
有的时候,想把后端产生的数据以变量的形式发送给前端,但是python的变量在html中显然是不好使的,那么怎样去解决这个矛盾呢?
这里我们可以定义一套语法规则,比如将html页面的 # #之间的东西替换成要显示的python变量就可以了
html页面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>show time</title> </head> <body> <h1>现在是:#times#</h1> </body> </html>
上面的 #times#中的times是个变量,它记录先当前的时刻,但是这样写最后在页面显示的护额的一对不会是当前时间,仍会是#times#
但是在把html页面返回给浏览器的之前可以先进行处理,就是把#times#用变量替换掉,那这时候显示的就是当前时间而不是#times#了
python页面
from wsgiref.simple_server import make_server def show_time(req): print(req) import time # 获取当前的时间 times = time.ctime() # 将要显示的页面打开 f = open("show_time.html", "rb") data = f.read() data = data.decode("utf8") # 替换掉里面的变量内容,也就是将# #之间的变量用相应的值替换掉 data = data.replace("#times#", str(times)) return data.encode("utf8") # 路由函数,解决URL过多问题 def router(): urlpatterns = [ ('/show_time', show_time), ] return urlpatterns def application(environ, start_response): print(environ["PATH_INFO"]) # 该键对应的值就是服务器名后面的路径,比如127.0.0.1:8080/xin,拿到的就是/xin start_response('200 OK', [('Content-Type', 'text/html')]) path = environ["PATH_INFO"] urlpattern = router() # 循环遍历里面的参数跟PATH_INFO匹配 func = None for item in urlpattern: if item[0] == path: func = item[1] break # 找到后跳出,不在循环后面的内容 if func: return [func(environ)] # 将客户端HTTP请求信息传进去 else: return [b'<h1>404</h1>'] httpd = make_server('', 8080, application) print('Serving HTTP ON PORT 8080...') # 开始监听HTTP请求 httpd.serve_forever()