web-mini框架的基本实现(一)

  

  本篇主要介绍WSGI-web-mini框架的基本实现,以及什么是WSGI协议、对于浏览器的资源请求服务端是如何处理的等相关知识;

一、浏览器资源请求

  我们知道浏览器和web服务器之间是基于HTTP协议进行文件的传输的,而根据请求消息的不同,服务器处理的流程也是不同的;如下图:

  通常而言:

   对于浏览器请求的静态资源,服务器在收到请求消息时(处理完后),会直接在服务器的本地磁盘中将信息(index.html页面)读取出来,直接发送给web浏览器;

   而对于动态资源的请求,服务器通常会调用web框架来处理,而web框架会根据用户的请求不同,从数据库中读取不同的信息将其替换自HTML模板中,并且返回给HTTP服务器,最后由服务器将消息发送给web浏览器;

 

二、WSGI协议

 2.1 为什么要使用WSGI协议呢?

  思考:在以前,选择  Python-web架构 会受制于可用的 web服务器,反之亦然。为了让web架构和服务器可以协同工作,但有可能面对(或者曾有过)下面的问题,当要把一个服务器和一个架构结合起来时,却发现他们不是被设计成协同工作的;

  那么,怎么可以不修改服务器和架构代码而确保可以在多个架构下运行web服务器呢?

      

 

  答案就是 Python Web Server Gateway Interface (或简称 WSGI,读作“wizgy”)。

  WSGI允许开发者将选择web框架和web服务器分开。可以混合匹配web服务器和web框架,选择一个适合的配对。比如,可以在Gunicorn 或者 Nginx/uWSGI 或者 Waitress上运行 Django, Flask, 或 Pyramid。真正的混合匹配,得益于WSGI同时支持服务器和架构;

 

 2.2 WSGI接口

  而通常web服务器必须具备WSGI接口,所有的现代Python Web框架都已具备WSGI接口,它让你不对代码作修改就能使服务器和特点的web框架协同工作。

  WSGI由web服务器支持,而web框架允许你选择适合自己的配对,但它同样对于服务器和框架开发者提供便利使他们可以专注于自己偏爱的领域和专长而不至于相互牵制。其他语言也有类似接口:java有Servlet API,Ruby 有 Rack。

  我们需在web框架内定义一个WSGI接口函数,而web服务器与框架进行协同工作时,只需通过该接口函数即可,其他的实现均封装在该框架的底部;该接口函数如下:

# WSGI接口函数
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

 参数:

  environ  :一个包含所有HTTP请求信息的dict对象;即告诉框架需要调取哪些动态资源页面;

  start_response :一个发送HTTP响应的函数。该函数相当于HTTP服务器给与框架的一个容器,而框架将头部信息通过参数的方式存放在该函数内,最后服务器再从中取出;

 返回值:

  返回的是请求的动态资源的响应消息的body部分;通常web框架将动态资源替换HTML模板的消息返回给服务器;

 

三、实例:

  其大致结构如下:

  

  web服务器:

import socket
import multiprocessing
import re
# import dynamic.mini_frame
import sys

class WSGIServer(object):
    def __init__(self,port,app,static_path):
        # 1.创建监听套接字
        self.server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) 
        # 2.为了服务端先断开时,客户端马上能链接
        self.server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
        
        # 3.绑定本地端口
        self.server_socket.bind(("",port))
        # 4.监听
        self.server_socket.listen(128)

        self.port = port
        self.application = app
        self.static_path = static_path

    def deal_with_static(self,new_socket):
        # 如果不是以.py结尾的文件,当成静态页面处理
        try:
            f=open("static"+ self.file_name,"rb")
        except:
            header = "HTTP/1.1 404 NOT FOUND FILE\r\n"
            header += "\r\n"
            body = "<h1>not found file <\h1>"
            response = header+body
            new_socket.send(response.encode("utf-8"))
        else:
            header="HTTP/1.1 200 OK\r\n"
            header += "\r\n"
            body = f.read()
            response = header.encode("utf-8") + body
            new_socket.send(response)

    def deal_with_dynamic(self,new_socket):
                """处理动态页面"""
        env = dict()
        env["PATH_INFO"] = self.file_name

                # web框架的接口函数
        body = self.application(env,self.set_response_header)

        # headr部分的拼接成指定格式
                header= "HTTP/1.1 %s"%self.status
        for temp in self.header:
            header += "%s:%s\r\n" % (temp[0], temp[1])
        header+="\r\n"
        print(header)

        new_socket.send(header.encode("utf-8"))
        new_socket.send(body.encode("utf-8"))

    def set_response_header(self,status,header):
                """作为接口函数的参数,相当于容器接收web框架返回的头部信息"""
        self.status = status;
        self.header = header;

    def serve_client(self,new_socket):
        """接收请求消息和发送相应消息"""
        # 1.接受请求消息 GET / HTTP/1.1
        request = new_socket.recv(1024).decode("utf-8")

        # 2.解析请求消息
        request_lines = request.splitlines()
        ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
        if ret:
            self.file_name = ret.group(1)
            if self.file_name =="/":
                self.file_name = "/index.html"
        # print(file_name)

        # 3.尝试打开文件,若文件存在则能打开,否则发送404
        if not self.file_name.endswith(".py"):
            # 处理静态网页
            self.deal_with_static(new_socket)

            # new_socket.close()
        else:
            # 处理动态网页
            self.deal_with_dynamic(new_socket)

        new_socket.close()



    def run_forever(self):
                """当成主函数"""
        while True:
            new_socket,client_addr = self.server_socket.accept()
            p = multiprocessing.Process(target=self.serve_client,args=(new_socket,))
            p.start()
            p.join()

            new_socket.close()
        self.server_socket.close()

def main():
    # 运行服务器时,给出指定的 端口和运行的框架
    if len(sys.argv)==3:
        try:
            port=int(sys.argv[1])  # 6968
            frame_app_name = sys.argv[2]  # mini_frame:application
        except Exception as e:
            print("输入方式有误,请按照以下方式运行:")
            print("python3 web_server.py 6968 mini_frame:application")
            return
    ret = re.match(r"([^:]+):(.*)",frame_app_name)

    # 获取框架文件名
    if ret:
        frame_name = ret.group(1)
        app_name = ret.group(2)
    else:
        print("您的输入方式有误,请按照上述方式运行")
        return

    with open("./web_server.conf","r") as f:
        conf_info = eval(f.read())   # 将字符串转为字典类型


    # 将导入动态数据的模块添加到python解释器的环境变量中
    sys.path.append(conf_info["dynamic_path"])
    print(sys.path)

    frame =__import__(frame_name)  # 导入指定的模块
    app = getattr(frame,app_name)  # 反射出该模块下的指定的函数app_name


    wsgi_server = WSGIServer(port,app,conf_info["static_path"])
    wsgi_server.run_forever()


if __name__ == '__main__':
    main()        
web服务端

 解析:

  1、在web服务器中,我们首先根据资源的请求不同,给出的处理方式不同即静态资源直接从本地磁盘读取,而动态资源交给web框架处理。

  2、在与web框架进行交互,调用了wsgi接口函数,传入了字典env,用来告诉服务器需求页面;以及函数set_response_header用来获取web框架传递的头部信息;并且将返回值作为body;整个与web框架的交互均是围绕着wsgi接口函数来实现的;

  3、随后便是让我们的服务器运行可以指定端口和web框架来运行,则定义了main函数下面的一些代码,通过sys.argv来获取到指定的端口和web_frame:application等,再将这些作为参数传入类 WSGIServer中来执行;

 

  mini-web框架

import re


def index():
    """显示index.py动态数据"""
    with open("./templates/index.html","r") as f:
        content=f.read()
        # print(content)
    my_stock_info = "这是是数据库的数据,未完待续。"
    content = re.sub(r"\{%content%\}", my_stock_info, content)
    return content


def center():
    """显示center动态数据"""
    with open("./templates/center.html","r") as f:
         content = f.read()
         # print(content)
    mys_stock_info = "这是数据库的数据,未完待续。"
    content = re.sub(r"\{%content%\} ",mys_stock_info,content)
    return content

def application(environ,start_response):
    start_response("200 OK\r\n",[('Content-Type', 'text/html;charset=utf8')])
    file_name=environ["PATH_INFO"]
    if file_name == "/index.py":
        return index()
    elif file_name == "/center.py":
        return center()
    else:
        start_response("400 NOT FOUND FILE\r\n",[('Content-Type', 'text/html;charset=utf8')])
        return "Sorry,the file you want get have not found;"
web—mini框架

  在该框架内:定义了wsgi接口函数,以及获取指定页面的函数,在下篇中我们将会介绍如何将数据库的内容导入;

  

  web_frame.conf 配置文件

{
    "static_path":"./static",
    "dynamic_path":"./dynamic"
}
View Code

  配置文件主要用来指定:动态资源及静态资源存储位置,当请求动态资源时,将这些路径添加至python解释器的环境变量中去,方便web框架模块的导入;

 

  run.sh脚本运行文件

python3 web_server.py 6868 mini_frame:application

  定义脚本运行文件,主要是方便运行,不至于每次写一大坨东西去执行;

 

  over~~~ 下篇中会实现路由的功能,并且替换成数据库中的数据~~~

 

转载于:https://www.cnblogs.com/littlefivebolg/p/9406383.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,让我来为您介绍一个简单的 Python Web 框架,它类似于 Django,名字叫做 MiniWebMiniWeb 实现了一个基本Web 框架功能,包括 URL 路由、请求处理、模板渲染等。下面是 MiniWeb 的代码示例: ```python # 引入必要的库 import re import os import sys import time import socket import select # 定义 MiniWeb 类 class MiniWeb: # 初始化方法 def __init__(self, host='', port=80): self.host = host self.port = port self.routes = [] # 添加路由方法 def route(self, pattern): def decorator(handler): self.routes.append((re.compile(pattern), handler)) return handler return decorator # 处理请求方法 def handle_request(self, request): # 提取请求方法和路径 method, path, _ = request.split(' ', 2) # 遍历路由规则 for pattern, handler in self.routes: # 如果路径匹配路由规则,则调用对应的处理函数 if pattern.match(path): return handler(request) # 如果没有匹配的路由规则,则返回 404 错误 return 'HTTP/1.1 404 Not Found\r\n\r\n' # 运行框架方法 def run(self): # 创建套接字并绑定地址 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((self.host, self.port)) server_socket.listen(5) # 循环等待客户端连接 while True: # 使用 select 函数监听套接字 readable, _, _ = select.select([server_socket], [], [], 1) if readable: # 接受客户端连接并处理请求 client_socket, client_address = server_socket.accept() request = client_socket.recv(1024).decode() response = self.handle_request(request) client_socket.sendall(response.encode()) client_socket.close() # 定义路由处理函数 web = MiniWeb() @web.route('^/$') def index(request): return 'HTTP/1.1 200 OK\r\n\r\nHello, World!' @web.route('^/hello/(.*)$') def hello(request): name = request.split('/')[2] return f'HTTP/1.1 200 OK\r\n\r\nHello, {name}!' # 运行框架 if __name__ == '__main__': web.run() ``` 以上是 MiniWeb 的代码示例,您可以通过调用 `route` 方法来添加路由规则,然后编写对应的路由处理函数。当客户端发起请求时,MiniWeb 会根据路由规则来调用对应的路由处理函数,并返回处理结果。 当然,MiniWeb 还需要进一步的完善和优化,例如添加静态文件处理、错误处理、中间件等功能。但它足以作为一个简单的 Python Web 框架示例,帮助您更好地理解 Web 框架实现原理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值