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

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

那么,怎么可以不修改服务器和架构代码而确保可以在多个架构下运行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 7290mini_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()


作者:IIronMan

链接:https://www.jianshu.com/p/f44ce2dba098

来源:简书

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,324评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,356评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,328评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,147评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,160评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,115评论 1 296
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,025评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,867评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,307评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,528评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,688评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,409评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,001评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,657评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,811评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,685评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,573评论 2 353

推荐阅读更多精彩内容

  • web静态服务器 服务端: 1.1.1显示固定的页面 参考代码: import socket from multi...
    chen_000阅读 2,074评论 0 1
  • 网络编程 一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运...
    go以恒阅读 2,007评论 0 6
  • =========================================================...
    _灯火阑珊处阅读 2,418评论 0 3
  • 网络 理论模型,分为七层物理层数据链路层传输层会话层表示层应用层 实际应用,分为四层链路层网络层传输层应用层 IP...
    FlyingLittlePG阅读 766评论 0 0
  • 谈论WEB编程的时候常说天天在写CGI,那么CGI是什么呢?可能很多时候并不会去深究这些基础概念,再比如除了CGI...
    __七把刀__阅读 2,196评论 2 11