美文网首页Python知识锦集
Python mini-web框架1:WSGI-mini-web

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

作者: IIronMan | 来源:发表于2019-01-01 01:52 被阅读4次

总体内容

  • 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服务器,反之亦然。如果架构和服务器可以协同工作,那就好了:


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


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


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

相关文章

网友评论

    本文标题:Python mini-web框架1:WSGI-mini-web

    本文链接:https://www.haomeiwen.com/subject/nsxplqtx.html