搭建一个简单的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()

  

 

posted @ 2018-06-30 11:04  Jin同学  阅读(731)  评论(0)    收藏  举报