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()


目录
相关文章
|
17天前
|
Java 测试技术 持续交付
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
本文重点讲解如何搭建App自动化测试框架的思路,而非完整源码。主要内容包括实现目的、框架设计、环境依赖和框架的主要组成部分。适用于初学者,旨在帮助其快速掌握App自动化测试的基本技能。文中详细介绍了从需求分析到技术栈选择,再到具体模块的封装与实现,包括登录、截图、日志、测试报告和邮件服务等。同时提供了运行效果的展示,便于理解和实践。
63 4
【入门思路】基于Python+Unittest+Appium+Excel+BeautifulReport的App/移动端UI自动化测试框架搭建思路
|
6天前
|
JSON 前端开发 API
使用Python和Flask构建简易Web API
使用Python和Flask构建简易Web API
|
6天前
|
缓存 API 数据库
Python哪个框架合适开发速卖通商品详情api?
在跨境电商平台速卖通的商品详情数据获取与整合中,Python 语言及其多种框架(如 Flask、Django、Tornado 和 FastAPI)提供了高效解决方案。Flask 简洁灵活,适合快速开发;Django 功能全面,适用于大型项目;Tornado 性能卓越,擅长处理高并发;FastAPI 结合类型提示和异步编程,开发体验优秀。选择合适的框架需综合考虑项目规模、性能要求和团队技术栈。
18 2
|
11天前
|
关系型数据库 数据库 数据安全/隐私保护
Python Web开发
Python Web开发
40 6
|
16天前
|
开发框架 前端开发 JavaScript
利用Python和Flask构建轻量级Web应用的实战指南
利用Python和Flask构建轻量级Web应用的实战指南
49 2
|
18天前
|
前端开发 JavaScript 开发工具
从框架到现代Web开发实践
从框架到现代Web开发实践
27 1
|
8天前
|
安全 API 数据库
Python哪个框架合适开发淘宝商品详情api?
在数字化商业时代,开发淘宝商品详情API成为企业拓展业务的重要手段。Python凭借其强大的框架支持,如Flask、Django、Tornado和FastAPI,为API开发提供了多样化的选择。本文探讨了这些框架的特点、优势及应用场景,帮助开发者根据项目需求选择最合适的工具,确保API的高效、稳定与可扩展性。
17 0
|
15天前
|
安全 API 网络架构
Python中哪个框架最适合做API?
本文介绍了Python生态系统中几个流行的API框架,包括Flask、FastAPI、Django Rest Framework(DRF)、Falcon和Tornado。每个框架都有其独特的优势和适用场景。Flask轻量灵活,适合小型项目;FastAPI高性能且自动生成文档,适合需要高吞吐量的API;DRF功能强大,适合复杂应用;Falcon高性能低延迟,适合快速API开发;Tornado异步非阻塞,适合高并发场景。文章通过示例代码和优缺点分析,帮助开发者根据项目需求选择合适的框架。
42 0
|
6月前
|
开发框架 开发者 Python
深入探究Python Web开发框架:Flask与Django
Python作为一种广泛应用于Web开发的编程语言,其拥有众多优秀的Web开发框架。本文将深入探讨其中两大知名框架——Flask与Django。通过对它们的概念与实践进行比较分析,帮助读者更好地理解和选择适合自己项目需求的Web开发框架。
|
6月前
|
前端开发 数据库 Python
Python Web 开发: 解释 Django 框架的 MVC 架构是什么?
Python Web 开发: 解释 Django 框架的 MVC 架构是什么?
127 0
下一篇
无影云桌面