Python mini-web框架1:WSGI-mini-web框架

简介: Python mini-web框架1:WSGI-mini-web框架

总体内容


  • 1、多进程-web服务器面向对象web服务器,返回的是静态界面
  • 2、静态资源、动态资源、web服务器支持动态解析
  • 3、实现很简单的框架,让web服务器支持
  • 4、模仿WSGI协议来做一个web服务器的框架
  • 5、通过传字典实现浏览器请求的资源不一样,响应的不一样
  • 6、给程序传递参数、添加web服务器的配置文件、添加shell功能


一、多进程-web服务器面向对象web服务器,返回的是静态界面


import socket
import multiprocessing
import re
class WSGIServer(object):
     def __init__(self):=
          """用来完成整体的控制"""
          # 1.创建套接字
          self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          # 2.绑定
          self.tcp_server_socket.bind(("192.168.3.6", 7790))
          # 3.变为监听套接字
          self.tcp_server_socket.listen(128)
     def server_client(self,new_socket):
          """为这个客户端返回数据"""
          # # 组织相应 头信息(header)
          # 1.接收浏览器发送过来的请求,即 http请求
          # GET / HTTP/1.1
          # ....
          request = new_socket.recv(1024).decode("utf-8")
          # print(request)
          request_lines = request.splitlines()
          print("")
          print(">"*20)
          print(request_lines)
          print("<" * 20)
          # GET /index.html HTTP/1.1
          # get post put del
          file_name = ""
          ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
          if ret:
             file_name = ret.group(1)
             print("file_name=%s" % file_name)
             print("*"*50,file_name)
             if file_name == "/":
                  file_name = "/index.html"
          # 2.返回http格式的数据,给浏览器
          # 2.1、准备发送给浏览器的数据---header
          try:
               f = open("./html"+file_name,"rb")
          except:
               response = "HTTP/1.1 404 NOT FOUND\r\n"
               response += "\r\n"
               response += "----file not found"
               new_socket.send(response.encode("utf-8"))
          else:
               print("-----------OK------------")
                html_content = f.read()
                f.close()
                response = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
                response += "\r\n"  # 用一个空的行与body进行隔开
           # 2.2、准备发送给浏览器的数据 ---body
           # 将response的header发送给浏览器
           new_socket.send(response.encode("utf-8"))
           # 将response的 body 发送给浏览器
           new_socket.send(html_content)
           # 3.关闭套接字
           new_socket.close()
      def run_server(self):
           while True:
                # 4.等待客户端的链接
                new_socket, client_addr = self.tcp_server_socket.accept()
                # 5.开辟一个进程为这个客户端服务
                p = multiprocessing.Process(target=self.server_client,args=(new_socket,))
                p.start()
                new_socket.close()
           # 6.关闭监听的套接字
           tcp_server_socket.close()
def main():
     wsgi_server = WSGIServer()
     wsgi_server.run_server()
if __name__ == '__main__':
      main()


二、静态资源、动态资源、web服务器支持动态解析



返回的是动态界面,也就是在上面的server_client代码中加一个判断,我们以请求的是 .py结尾 来判断是是动态页面请求

def server_client(self,new_socket):
     """为这个客户端返回数据"""
     # # 组织相应 头信息(header)
     # 1.接收浏览器发送过来的请求,即 http请求
     # GET / HTTP/1.1
     # ....
     request = new_socket.recv(1024).decode("utf-8")
     # print(request)
     request_lines = request.splitlines()
     print("")
     print(">"*20)
     print(request_lines)
     print("<" * 20)
     # GET /index.html HTTP/1.1
     # get post put del
     file_name = ""
     ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
     if ret:
         file_name = ret.group(1)
         print("file_name=%s" % file_name)
         print("*"*50,file_name)
         if file_name == "/":
             file_name = "/index.html"
     # 2.返回http格式的数据,给浏览器
     # 2.1、准备发送给浏览器的数据---header
     if not file_name.endswith(".py"):
        try:
            f = open("./html"+file_name,"rb")
        except:
            response = "HTTP/1.1 404 NOT FOUND\r\n"
            response += "\r\n"
            response += "----file not found"
            new_socket.send(response.encode("utf-8"))
        else:
            print("-----------OK------------")
            html_content = f.read()
            f.close()
            response = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
            response += "\r\n"  # 用一个空的行与body进行隔开
            # 2.2、准备发送给浏览器的数据 ---body
            # 将response的header发送给浏览器
            new_socket.send(response.encode("utf-8"))
            # 将response的 body 发送给浏览器
            new_socket.send(html_content)
     else:
        # 2.2 如果是以 .py 结尾请求就认为是动态资源请求
        header = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
        header += "\r\n"  # 用一个空的行与body进行隔开
        body = "HHHHH"
        response = header + body
        # 准备发送给浏览器的数据 ---body
        # 将response发送给浏览器
        new_socket.send(response.encode("utf-8"))
    # 3.关闭套接字
    new_socket.close()


三、实现很简单的框架,让web服务器支持



提示:也就是写一个共用模块,把请求的数据传到这个模块,然后再模块内进行分析,返回相应的数据,我们把这个模块定义为 mini_frame ,模块内的代码如下

import time
def login():
      return "welcome to our login ......time:%s"% time.ctime()
def register():
      return "welcome to our register ......time:%s"% time.ctime()
def application(file_name):
      if file_name == "/login.py":
             return login()
      elif file_name == "/register.py":
             return  register()
      else:
             return "not found you page..."


把之前 def server_client 里面判断是 .py 结尾的代码里面的body改为如下代码

body = mini_frame.application(file_name)


四、模仿WSGI协议来做一个web服务器的框架



  • 4.1、先了解一下 WSGI 协议
  • 怎么在你刚建立的Web服务器上运行一个Django应用和Flask应用,如何不做任何改变而适应不同的web架构呢?
  • 在以前,选择 Python web 架构会受制于可用的web服务器,反之亦然。如果架构和服务器可以协同工作,那就好了:


image.png



但有可能面对(或者曾有过)下面的问题,当要把一个服务器和一个架构结合起来时,却发现他们不是被设计成协同工作的:



image.png

那么,怎么可以不修改服务器和架构代码而确保可以在多个架构下运行web服务器呢?答案就是 Python Web Server Gateway Interface (或简称 WSGI,读作“wizgy”)。


image.png

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


image.png

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


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


  • 4.2、模仿WSGI 服务器代码的具体实现简单的叙述下WSGI的思路:1.在服务器有一个接收header头的方法,我们在此定义为set_response_header,2.在自定义的web框架,调用服务器的set_response_header方法,并返回 body内容,具体的代码如下


  • 服务器


if not file_name.endswith(".py"):
      pass
else{
      # 2.2 如果是以 .py 结尾请求就认为是动态资源请求
      evn = dict()
      body = mini_wsgi_frame.application(evn,self.set_response_header)
      header = "HTTP/1.1 %s\r\n"%self.status  # 200表示找到这个资源
      for temp in self.headers:
           header += "%s:%s\r\n"%(temp[0],temp[1])
      header += "\r\n"  # 用一个空的行与body进行隔开
      response = header + body
      # 准备发送给浏览器的数据 ---body
      # 将response发送给浏览器
      new_socket.send(response.encode("utf-8"))
}
def set_response_header(self,status,headers):
      self.status = status
      self.headers = [("server","mini_web v9.2")]
      self.headers += headers
  • 自定义web框架代码: mini_wsgi_frame


def application(environ,start_response):
      start_response('200 OK',[('text/html;charset=utf-8')])
      return  'Hello World!'


五、通过给web服务器传字典实现浏览器请求的资源不一样,响应的不一样


  • 服务器:只写 是 .py 里面的代码


if not file_name.endswith(".py"):
     # 2.2 如果是以 .py 结尾请求就认为是动态资源请求
     evn = dict()
     evn["PATH_INFO"] = file_name
     body = dynamic.mini_wsgi_true_frame.application(evn,self.set_response_header)
     header = "HTTP/1.1 %s\r\n"%self.status  # 200表示找到这个资源
     for temp in self.headers:
         header += "%s:%s\r\n"%(temp[0],temp[1])
         header += "\r\n"  # 用一个空的行与body进行隔开
         response = header + body
         # 准备发送给浏览器的数据 ---body
         # 将response发送给浏览器
         new_socket.send(response.encode("utf-8"))
  • WSGI框架代码:mini_wsgi_true_frame.py


import time
def login():
     return "welcome to our login ......time:%s"% time.ctime()
def register():
     return "welcome to our register ......time:%s"% time.ctime()
def application(environ,start_response):
     start_response('200 OK',[('Content-Type','text/html;charset=utf-8')])
     file_name = environ["PATH_INFO"]
     if file_name == "/login.py":
           return login()
     elif file_name == "/register.py":
           return register()
     else:
           return 'Hello World! 我爱中华'


六、给程序传递参数、添加web服务器的配置文件、添加shell功能



  • 6.1、给程序传递参数(把上面代码中的端口写活),达到用终端运行


mini_wsgi_true_frame.py


class WSGIServer(object):
     def __init__(self,port):
           """用来完成整体的控制"""
           # 1.创建套接字
           self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
           # 2.绑定
           self.tcp_server_socket.bind(("192.168.3.6", port))
           # 3.变为监听套接字
           self.tcp_server_socket.listen(128)
def main():
     if len(sys.argv) == 2:
         try:
              port = int(sys.argv[1]) # 8890
         except Exception as ret:
              print("端口输入错误")
              return
      else:
          print("请按照以下方式运行")
          print("python3 xxx.py 7890")
          return 
      wsgi_server = WSGIServer(port)
      wsgi_server.run_server()
if __name__ == '__main__':
      main()
  • 6.2、添加web服务器的配置文件之前的代码还要在服务器里面导入 import dynamic,这个不太好,我们要写活,有可能,换框架,我们需要导入框架的名字来变成活的,那么我们就需要设置 配置文件,起名为 web_server.conf
  • web_server.conf里面的代码如下:提示这是一个字符串


{
   "static_path":"./static",
   "dynamic_path":"./dynamic"
}
  • 核心代码如下


def main():
      if len(sys.argv) == 3:
          try:
              port = int(sys.argv[1])  # 7890
              frame_app_name = sys.argv[2]  # mini_frame:application
          except Exception as ret:
              print("端口输入错误。。。。。")
              return
      else:
          print("请按照以下方式运行:")
          print("python3 xxxx.py 7890 mini_frame:application")
          return
      # mini_wsgi_true_frame:application
      ret = re.match(r"([^:]+):(.*)", frame_app_name)
      if ret:
           frame_name = ret.group(1)  # mini_frame
           app_name = ret.group(2)  # application
      else:
           print("请按照以下方式运行:")
           print("python3 xxxx.py 7890 mini_wsgi_true_frame:application")
           return
      with open("./web_server.conf") as f:
           conf_info = eval(f.read())
      # 此时 conf_info是一个字典里面的数据为:
      # {
      #     "static_path":"./static",
      #     "dynamic_path":"./dynamic"
      # }
      # 添加当前文件爱夹的路径
      sys.path.append(conf_info['dynamic_path'])
      # import frame_name --->找frame_name.py
      frame = __import__(frame_name)  # 返回值标记这 导入的这个模板
      app = getattr(frame, app_name)  # 此时app就指向了 dynamic/mini_frame模块中的application这个函数
      # print(app)
      wsgi_server = WSGIServer(port,app, conf_info['static_path'])
      wsgi_server.run_server()
if __name__ == '__main__':
      main()
  • 6.3、添加shell功能
    创建run.sh文件


python3 JK_06_webServer.py 7290 mini_wsgi_true_frame:application
  • 运行的时候,cd run.sh所在的文件夹,然后输入 ./run.sh,回车键只有直接运行

提示:run.sh 有可能没有执行的权限,可以输入 chmod +x run.sh来增加 x 执行的权限


  • 6.4、服务器完整的代码


import socket
import multiprocessing
import re
import sys
class WSGIServer(object):
     def __init__(self,port, app, static_path):
         """用来完成整体的控制"""
         # 1.创建套接字
         self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
         # 2.绑定
         self.tcp_server_socket.bind(("192.168.3.6", port))
         # 3.变为监听套接字
         self.tcp_server_socket.listen(128)
         self.application = app
         self.static_path = static_path
     def server_client(self,new_socket):
            """为这个客户端返回数据"""
            # # 组织相应 头信息(header)
            # 1.接收浏览器发送过来的请求,即 http请求
            # GET / HTTP/1.1
            # ....
            request = new_socket.recv(1024).decode("utf-8")
            # print(request)
           request_lines = request.splitlines()
           print("")
           print(">"*20)
           print(request_lines)
           print("<" * 20)
           # GET /index.html HTTP/1.1
           # get post put del
           file_name = ""
           ret = re.match(r"[^/]+(/[^ ]*)",request_lines[0])
           if ret:
              file_name = ret.group(1)
              print("file_name=%s" % file_name)
              print("*"*50,file_name)
              if file_name == "/":
                   file_name = "/index.html"
           # 2.返回http格式的数据,给浏览器
           # 2.1、准备发送给浏览器的数据---header
           if not file_name.endswith(".py"):
               try:
                   f = open(self.static_path+file_name,"rb")
               except:
                   response = "HTTP/1.1 404 NOT FOUND\r\n"
                   response += "\r\n"
                   response += "----file not found"
                   new_socket.send(response.encode("utf-8"))
               else:
                   print("-----------OK------------")
                   html_content = f.read()
                   f.close()
                   response = "HTTP/1.1 200 OK\r\n"  # 200表示找到这个资源
                   response += "\r\n"  # 用一个空的行与body进行隔开
                   # 2.2、准备发送给浏览器的数据 ---body
                   # 将response的header发送给浏览器
                   new_socket.send(response.encode("utf-8"))
                   # 将response的 body 发送给浏览器
                   new_socket.send(html_content)
            else:
                # 2.2 如果是以 .py 结尾请求就认为是动态资源请求
                evn = dict()
                evn["PATH_INFO"] = file_name
                body = self.application(evn,self.set_response_header)
                header = "HTTP/1.1 %s\r\n"%self.status  # 200表示找到这个资源
                for temp in self.headers:
                      header += "%s:%s\r\n"%(temp[0],temp[1])
                header += "\r\n"  # 用一个空的行与body进行隔开
                response = header + body
                # 准备发送给浏览器的数据 ---body
                # 将response发送给浏览器
                new_socket.send(response.encode("utf-8"))
            # 3.关闭套接字
            new_socket.close()
       def set_response_header(self,status,headers):
            self.status = status
            self.headers = [("server","mini_web v9.2")]
            self.headers += headers
       def run_server(self):
            while True:
               # 4.等待客户端的链接
               new_socket, client_addr = self.tcp_server_socket.accept()
               # 5.开辟一个进程为这个客户端服务
               p = multiprocessing.Process(target=self.server_client,args=(new_socket,))
               p.start()
               new_socket.close()
            # 6.关闭监听的套接字
            tcp_server_socket.close()
def main():
     if len(sys.argv) == 3:
          try:
             port = int(sys.argv[1])  # 7890
             frame_app_name = sys.argv[2]  # mini_frame:application
          except Exception as ret:
             print("端口输入错误。。。。。")
             return
     else:
          print("请按照以下方式运行:")
          print("python3 xxxx.py 7890 mini_frame:application")
          return
     # mini_wsgi_true_frame:application
     ret = re.match(r"([^:]+):(.*)", frame_app_name)
     if ret:
         frame_name = ret.group(1)  # mini_frame
         app_name = ret.group(2)  # application
     else:
         print("请按照以下方式运行:")
         print("python3 xxxx.py 7890 mini_wsgi_true_frame:application")
         return
     with open("./web_server.conf") as f:
         conf_info = eval(f.read())
     # 此时 conf_info是一个字典里面的数据为:
     # {
     #     "static_path":"./static",
     #     "dynamic_path":"./dynamic"
     # }
     # 添加当前文件爱夹的路径
     sys.path.append(conf_info['dynamic_path'])
     # import frame_name --->找frame_name.py
     frame = __import__(frame_name)  # 返回值标记这 导入的这个模板
     app = getattr(frame, app_name)  # 此时app就指向了 dynamic/mini_frame模块中的application这个函数
     # print(app)
     wsgi_server = WSGIServer(port,app, conf_info['static_path'])
     wsgi_server.run_server()
if __name__ == '__main__':
     main()


目录
相关文章
|
2天前
|
数据采集 存储 中间件
Python进行网络爬虫:Scrapy框架的实践
【8月更文挑战第17天】网络爬虫是自动化程序,用于从互联网收集信息。Python凭借其丰富的库和框架成为构建爬虫的首选语言。Scrapy作为一款流行的开源框架,简化了爬虫开发过程。本文介绍如何使用Python和Scrapy构建简单爬虫:首先安装Scrapy,接着创建新项目并定义爬虫,指定起始URL和解析逻辑。运行爬虫可将数据保存为JSON文件或存储到数据库。此外,Scrapy支持高级功能如中间件定制、分布式爬取、动态页面渲染等。在实践中需遵循最佳规范,如尊重robots.txt协议、合理设置爬取速度等。通过本文,读者将掌握Scrapy基础并了解如何高效地进行网络数据采集。
26 6
|
4天前
|
机器学习/深度学习 JSON API
【Python奇迹】FastAPI框架大显神通:一键部署机器学习模型,让数据预测飞跃至Web舞台,震撼开启智能服务新纪元!
【8月更文挑战第16天】在数据驱动的时代,高效部署机器学习模型至关重要。FastAPI凭借其高性能与灵活性,成为搭建模型API的理想选择。本文详述了从环境准备、模型训练到使用FastAPI部署的全过程。首先,确保安装了Python及相关库(fastapi、uvicorn、scikit-learn)。接着,以线性回归为例,构建了一个预测房价的模型。通过定义FastAPI端点,实现了基于房屋大小预测价格的功能,并介绍了如何运行服务器及测试API。最终,用户可通过HTTP请求获取预测结果,极大地提升了模型的实用性和集成性。
14 1
|
5天前
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
|
4天前
|
SQL 前端开发 关系型数据库
Python之Web框架Django
Python之Web框架Django
8 0
|
5天前
|
设计模式 API Python
Python Web:Django、Flask和FastAPI框架对比
Python Web:Django、Flask和FastAPI框架对比
13 0
|
8天前
|
算法 程序员 开发工具
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
在学习Python的旅程中你是否正在“绝望的沙漠”里徘徊? 学完基础教程的你,是否还在为选择什么学习资料犹豫不决,不知从何入手,提高自己?
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
|
1天前
|
Shell 数据处理 C++
【震撼揭秘】Python正则VS Shell正则:一场跨越编程边界的史诗级对决!你绝不能错过的精彩较量,带你领略文本处理的极致魅力!
【8月更文挑战第19天】正则表达式是文本处理的强大工具,在Python与Shell中有广泛应用。两者虽语法各异,但仍共享许多基本元素,如`.`、`*`及`[]`等。Python通过`re`模块支持丰富的功能,如非捕获组及命名捕获组;而Shell则依赖`grep`、`sed`和`awk`等命令实现类似效果。尽管Python提供了更高级的特性和函数,Shell在处理文本文件方面仍有其独特优势。选择合适工具需根据具体需求和个人偏好决定。
|
6天前
|
算法 程序员 开发工具
百万级Python讲师又一力作!Python编程轻松进阶,豆瓣评分8.1
在学习Python的旅程中你是否正在“绝望的沙漠”里徘徊? 学完基础教程的你,是否还在为选择什么学习资料犹豫不决,不知从何入手,提高自己?
|
3天前
|
数据采集 存储 人工智能
掌握Python编程:从基础到进阶的实用指南
【8月更文挑战第17天】 本文旨在通过浅显易懂的语言和实际案例,为初学者和有一定基础的开发者提供一条清晰的Python学习路径。我们将从Python的基本语法入手,逐步深入到面向对象编程、数据科学应用及网络爬虫开发等高级主题。每个部分都配备了代码示例和实操建议,确保读者能够将理论知识转化为实际能力。无论你是编程新手,还是希望提升Python技能的开发者,这篇文章都将为你打开一扇通往高效编程世界的大门。
8 2
|
8天前
|
Python
python Process 多进程编程
python Process 多进程编程
19 1