如何搭建一个简易的Web框架

阿里云服务器优惠

Web框架本质

什么是Web框架, 如何自己搭建一个简易的Web框架?其实, 只要了解了HTTP协议, 这些问题将引刃而解.

  简单的理解: 所有的Web应用本质上就是一个socket服务端, 而用户的浏览器就是一个socket客户端.

用户在浏览器的地址栏输入网址, 敲下回车键便会给服务端发送数据, 这个数据是要遵守统一的规则(格式)的, 这个规则便是HTTP协议. HTTP协议主要规定了客户端和服务器之间的通信格式

  >浏览器收到的服务器响应的相关信息可以在浏览器调试窗口(F12键开启)的Network标签页中查看, 点击view source即可以查看原始响应数据(有些网页可能并没有该项)
访问码云网站的原始响应数据(节选)
`
  HTTP/1.1 200 OK

  Date: Thu, 16 May 2019 13:30:59 GMT

  Content-Type: text/html; charset=utf-8

  Transfer-Encoding: chunked

  Connection: keep-alive`

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

**HTTP GET请求的格式 **

image.png

**HTTP 响应的格式 **

image.png

以上内容总结为一句话便是: 要使自己写的Web server端正常运行起来, 必须要使我们自己的Web server端在给客户端回复消息时按照HTTP协议的规则加上响应状态行

自定义Web框架

一 响应指定内容的Web框架

  浏览器访问127.0.0.1:9001将返回Hello World标题字样

import socket  # 导入socket模块

def main():
  # 实例化socket对象
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  # 绑定IP地址与端口
  sock.bind(('127.0.0.1', 9001))
  # 监听
  sock.listen()
  while True:
    conn, addr = sock.accept()
    data = conn.recv(1024)
    str = data.decode("UTF-8").strip(" ")
    print("浏览器请求信息>>>", str)
    # 如果浏览器请求信息非空则进行回复
    if str:
      # 给回复的消息加上响应状态行
      conn.send(b"HTTP/1.1 200 OK\r\n\r\n")
      conn.send(b"<h1>Hello World</h1>")
      conn.close()
    # 否则跳过本次循环, 开始下一次循环
    else:
      continue

if __name__ == "__main__":
  main()
  ```

### 二 响应HTML文件的Web框架
    (1) 首先创建一个html文件  

    一个展示标题与当前时间的网页, 命名为index.html




  
  
  
  

  index


  

欢迎访问简易版Web框架主页

  
  


  在该html文件中可添加img标签, 其src属性值如果是网络地址也是可以直接在浏览器上现实的

  在该html文件中的css样式与js操作同样可以直接在浏览器上显示出来

    (2) 准备服务端程序, 文件命名为server.py  

import socket # 导入socket模块
import os # 导入os模块

def main():
  # 利用os模块拼接路径
html_path = os.path.join(os.path.dirname(file), "index.html")
  # 实例化socket对象
  sk = socket.socket()
  # 绑定IP地址与端口
  sk.bind(('127.0.0.1',9001))
  # 监听
  sk.listen()
  # 计数
  i = 1
  while True:
    # 等待浏览器连接获取连接
    conn, _ = sk.accept()
    # 接收浏览器请求
    data = conn.recv(1024)
    # 将浏览器请求转换为字符串并格式化
    str = data.decode('utf-8').strip(" ")
    # 打印浏览器响应
    print('浏览器请求信息>>>:', str, i)
    # 计数自加
    i += 1
    # 如果浏览器请求内容并不为空, 响应浏览器请求
    if str:
      # 为响应的数据加上相应状态行
      conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
      # 以bytes数据类型打开html文件
      with open(html_path,'rb') as f:
        # 读取数据
        data = f.read()
        # 发送html文件数据
        conn.send(data)
      # 关闭与浏览器的连接
      conn.close()
    # 若浏览器请求信息为空则关闭连接并跳过本次循环, 开始下一次循环
    else:
      conn.close()
      continue

if name == "main":
main()

  注意: 该例子使用相对路径, index.html与server.py需在同一目录下

### 三 根据浏览器请求响应数据的Web框架
  以上简易的框架基本上都是指定了要给浏览器返回什么数据, 这样肯定满足不了我们的需求, 那么如何才能根据浏览器的请求, 响应相对应的数据呢?

  CSS, JS, 图片等文件都叫做网站的静态文件

    (1) 为了测试, 首先创建一个html文件, 命名为index.html  







index



欢迎访问Web框架首页

把鼠标移到上面


     (2) 接着创建一个CSS文件, 命名为css.css  

div
{
/* 初始化元素背景色为绿色 /
background-color:green;
/
初始化元素宽200px /
width:200px;
/
初始化元素高200px /
height:200px;
/
初始化元素内填充40px /
padding:40px;
/
初始化字体颜色为白色 */
color:#ffffff;
}

(3) 再创建一个JS文件, 命名为js.js  

// 定义鼠标覆盖事件触发函数
function mOver(obj)
{
// 文字替换为"谢谢"
obj.innerHTML="谢谢"
// 背景颜色更改为红
obj.style.backgroundColor= "red";
}
// 定义鼠标非覆盖状态事件触发函数
function mOut(obj)
{
// 文字替换为"把鼠标以到上面"
obj.innerHTML="把鼠标移到上面"
// 背景颜色更改为绿
obj.style.backgroundColor= "green";
}

      (4) 准备服务端程序, 文件命名为server.py  

import os # 导入os模块
import socket # 导入socket模块

导入线程模块

from threading import Thread

实例化socket对象

server = socket.socket()

绑定IP及端口

server.bind(("127.0.0.1", 9001))
server.listen()

路径拼接

html_path = os.path.join(os.path.dirname(file), "index.html")
css_path = os.path.join(os.path.dirname(file), "css.css")
js_path = os.path.join(os.path.dirname(file), "js.js")

def html(conn):
"""
响应"/"请求
"""
conn.send(b'HTTP/1.1 200 ok \r\n\r\n')
with open(html_path, mode="rb") as f:
content = f.read()
conn.send(content)
conn.close()

def css(conn):
"""
响应"/css.css"请求
"""
conn.send(b"HTTP/1.1 200 ok \r\n\r\n")
with open(css_path, mode="rb") as f:
content = f.read()
conn.send(content)
conn.close()

def js(conn):
"""
响应"/js.js"请求
"""
conn.send(b"HTTP/1.1 200 ok \r\n\r\n")
with open(js_path, mode="rb") as f:
content = f.read()
conn.send(content)
conn.close()

def NotFound(conn):
conn.send(b"HTTP/1.1 200 ok \r\n\r\n")
conn.send(b"

404NotFound!

")

请求列表

request_list = [
("/", html),
("/css.css", css),
("/js.js", js)
]

def get(conn):
"""
处理响应函数
"""
try: # 异常处理
req = conn.recv(1024).decode("UTF-8")
req = req.split("\r\n")[0].split()[1]
# 打印浏览器请求
print(req)
except IndexError:
pass

# 遍历请求列表进行响应
for request in request_list:
    # 若浏览器请求信息等于请求列表中的项,则进行响应
    # 判断服务端是否能够进行响应
    if req == request[0]:
        # 获取线程对象, 实现并发
        t = Thread(target=request[1], args=(conn, ))
        # 启动线程
        t.start()
        # 响应后结束遍历
        break
    else:  # 若本次循环未匹配则跳过本次循环开始下一次
        continue
else:  # 若所有请求皆不匹配则调用NotFound函数, 表示无法响应
    NotFound(conn)

def main():
while True:
# 利用线程实现并发
# 获取TCP连接
conn, _ = server.accept()
t = Thread(target=get, args=(conn,))
t.start()

if name == "main":
main()

   注意: 该例子使用相对路径, index.html, css.css, js.js与server.py需在同一目录下

### 四 进阶版Web框架
  以上的几版Web框架比较基础, 一些定义的函数使用起来也比较繁琐, 可定制性很差, 修改起来也比较困难. 

  利用Python提供的一些模块可以简化一些步骤, 并且使框架的可定制性更好, 可以方便其他人进行定制使用

    结构示意图  

![image.png](https://i.loli.net/2021/02/25/ySf8qHBWVun74bj.png)

    文件结构  

![image.png](https://i.loli.net/2021/02/25/zYwybAJNgniRjrE.png)

### 构建Web框架
    (1) 构建目录  

  新建文件夹frame

    1) 文件夹内创建__init__.py文件(内容为空)

    2) 文件夹内新建文件夹file

    (2) 准备html文件  

  index.html文件






index


欢迎访问简易版Web框架主页

@