Python高階 -- 10 WSGI、mini_frame(web框架)

DJTUDaker發表於2018-03-18

一、模擬瀏覽器訪問指定頁面


1、使用多執行緒實現動態訪問指定頁面


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        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:
            html_content = f.read()
            f.close()
            # 設定響應頭
            response = "HTTP/1.1 200 OK \r\n"
            response += "\r\n"
            # 設定響應體
            new_socket.send(response.encode("utf-8"))
            new_socket.send(html_content)

        # 關閉套接字
        new_socket.close()

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


2、實現web伺服器返回動態頁面


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing


class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結尾,那麼就認為是動態資源的請求
            header = "HTTP/1.1 200 OK \r\n"
            header += "\r\n"
            body = "give you back"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


3、實現web伺服器和處理邏輯程式碼分開的功能


server-web.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結尾,那麼就認為是動態資源的請求
            header = "HTTP/1.1 200 OK \r\n"
            header += "\r\n"
            body = mini_frame.application(file_name)

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


def login():
    return "this is login"

def register():
    return "this is register"

def profile():
    return "this is profile"

def application(file_name):
    if file_name == "/login.py":
        return login()
    elif file_name == "/register.py":
        return register()
    else:
        return "the page you found is not exist"


二、WSGI


1、什麼是WSGI協議


(1)、瀏覽器請求頁面的過程



(2)、WSGI的好處


        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。


(3)、WSGI介面的定義


WSGI介面定義非常簡單,它只要求Web開發者實現一個函式,就可以響應HTTP請求。介面Demo如下:


def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'


函式解釋:上面的application()函式就是符合WSGI標準的一個HTTP處理函式,它接收兩個引數:


        environ:一個包含所有HTTP請求資訊的dict物件;


        start_response:一個傳送HTTP響應的函式。


        整個application()函式本身沒有涉及到任何解析HTTP的部分,也就是說,把底層web伺服器解析部分和應用程式邏輯部分進行了分離,這樣開發者就可以專心做一個領域了。


        這個application()函式怎麼呼叫?如果我們自己呼叫,兩個引數environ和start_response我們沒法提供,返回的str也沒法發給瀏覽器。所以application()函式必須由WSGI伺服器來呼叫。有很多符合WSGI規範的伺服器。而我們此時的web伺服器專案的目的就是做一個既能解析靜態網頁還可以解析動態網頁的伺服器


2、實現引入WSGI協議的web伺服器功能


web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結尾,那麼就認為是動態資源的請求
            evn = dict()
            body = mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


def application(environ, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return "Hello World!!!"


3、web伺服器與邏輯程式碼分離的動態訪問指定頁面


web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結尾,那麼就認為是動態資源的請求
            evn = dict()

            # 將使用者訪問的頁面資訊放入字典中,傳遞給框架
            evn['PATH_INFO'] = file_name

            body = mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


# coding=UTF-8
def index():
    return "這是index頁面"

def login():
    return "這是登入頁面"

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html;charset=utf-8')])
    
    file_name = env['PATH_INFO']

    if file_name == "/index.py":
        return index()
    elif file_name == "/login.py":
        return login()
    else:
        return "我愛你中國..."


4、mini_frame獲取模版頁面資料


靜態資原始檔位置:https://download.csdn.net/download/wingzhezhe/10295146




web-server.py


# coding=UTF-8
import socket
import re
import multiprocessing
import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", 8080))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./static" + 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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結尾,那麼就認為是動態資源的請求
            evn = dict()

            # 將使用者訪問的頁面資訊放入字典中,傳遞給框架
            evn['PATH_INFO'] = file_name

            body = dynamic.mini_frame.application(evn, self.set_response_header)

            header = "HTTP/1.1 %s \r\n" % self.status

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode(""))

        # 關閉套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""
    wsgi_server = WSGIServer()
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


mini_frame.py


# coding=UTF-8
def index():
    with open("./templates/index.html") as f:
        return f.read()

def center():
    with open("./templates/center.html") as f:
        return f.read()

def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html;charset=utf-8')])
    
    file_name = env['PATH_INFO']

    if file_name == "/index.py":
        return index()
    elif file_name == "/center.py":
        return center()
    else:
        return "我愛你中國..."


5、替換模版中的資料


修改index.html這種的資料




修改mini_frame.py中的程式碼




訪問瀏覽器顯示效果




6、執行執行命令時動態指定伺服器埠和要呼叫的框架名稱


web-server.py中程式碼




# coding=UTF-8
import socket
import re
import sys
import multiprocessing
# import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self, port, app):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", port))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

        self.application = app

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        if not file_name.endswith(".py"):
            try:
                f = open("./static" + 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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                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

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""

    # sys模組中的argv:獲取python命令列中的資料,
    # 即:如果命令列是 python3 test.py aaa bbb  則,獲取到的sys.argv為['test.py', 'aaa','bbb']
    
    if len(sys.argv) == 3:
        try:
            # 獲取引數中的埠號
            port = int(sys.argv[1])

            # 獲取引數中的框架名
            frame_py_name = sys.argv[2] # py_name:fun_name
        except Exception as e:
            print("埠格式有誤")
            return
    else:
        print("輸入的命令列格式不正確,請按照一下方式執行:")
        print("python3 py檔案 埠號 框架的檔名:方法名")
        return 

    """使用正則獲取框架名和方法名"""
    ret = re.match(r"([^:]+):(.*)", frame_py_name)
    if ret:
        frame_name = ret.group(1)
        fun_name = ret.group(2)
        

    else:
        print("輸入的命令列格式不正確,請按照一下方式執行:")
        print("python3 py檔案 埠號 框架的檔名:方法名")
        return 
    
    """定位到模組所在的資料夾"""
    sys.path.append("./dynamic")

    """ import 可以寫在函式中 """

    frame = __import__(frame_name)  # 該方法返回值標記著匯入的模組物件

    # 在模組物件中根據名稱找到對應的方法
    app = getattr(frame, fun_name)


    wsgi_server = WSGIServer(port, app)
    wsgi_server.run_forever()


if __name__ == "__main__":
    main()


執行命令


root@ubuntu:~/Desktop/web/08-執行伺服器時指定埠以及框架# python3 web-server.py 8080 mini_frame:application


7、讓web伺服器支援配置檔案


在web-server.py同級資料夾中新增配置檔案web-server.conf




修改web-server.py中的程式碼




# coding=UTF-8
import socket
import re
import sys
import multiprocessing
# import dynamic.mini_frame

class WSGIServer(object):
    def __init__(self, port, app, static_path):
        # 1.建立套接字物件
        self.tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        # 2.繫結埠號
        self.tcp_server_socket.bind(("", port))

        # 3.變為監聽套接字
        self.tcp_server_socket.listen(128)

        self.application = app
        self.static_path = static_path

    def service_client(self, new_socket):
        """為這個客戶端返回資料"""

        # 1.接收瀏覽器傳送的http請求
        request = new_socket.recv(1024).decode("utf-8")

        request_lines = request.splitlines()
        #print("")
        #print(">" * 20)
        #print(request_lines)

        file_name = ""
        ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])
        #print(ret)
        if ret:
            file_name = ret.group(1)
            print(file_name)
            if file_name == "/":
                file_name = "/index.html"

        # 2.返回http格式的資料給瀏覽器
        # 2.1判斷請求名是否是以.py結尾的,如果不是,就說明訪問的是靜態資源(html css png ...)
        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:
                html_content = f.read()
                f.close()
                # 設定響應頭
                response = "HTTP/1.1 200 OK \r\n"
                response += "\r\n"
                # 設定響應體
                new_socket.send(response.encode("utf-8"))
                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

            for temp in self.headers:
                header += "%s:%s\r\n" % (temp[0], temp[1])

            header += "\r\n"

            response = header + body
            # 傳送給瀏覽器
            new_socket.send(response.encode("utf-8"))

        # 關閉套接字
        new_socket.close()
    
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = headers

    def run_forever(self):
        """用來完成整體的控制"""
        while True:
            # 等待新的客戶端進行連結
            new_socket, client_addr = self.tcp_server_socket.accept()

            # 為這個客戶端服務
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()

            new_socket.close()

        # 關閉監聽套接字
        self.tcp_server_socket.close()
            


def main():
    """控制整體,建立一個web伺服器物件,呼叫物件中的run_forever方法執行"""

    # sys模組中的argv:獲取python命令列中的資料,
    # 即:如果命令列是 python3 test.py aaa bbb  則,獲取到的sys.argv為['test.py', 'aaa','bbb']
    
    if len(sys.argv) == 3:
        try:
            # 獲取引數中的埠號
            port = int(sys.argv[1])

            # 獲取引數中的框架名
            frame_py_name = sys.argv[2] # py_name:fun_name
        except Exception as e:
            print("埠格式有誤")
            return
    else:
        print("輸入的命令列格式不正確,請按照一下方式執行:")
        print("python3 py檔案 埠號 框架的檔名:方法名")
        return 

    """使用正則獲取框架名和方法名"""
    ret = re.match(r"([^:]+):(.*)", frame_py_name)
    if ret:
        frame_name = ret.group(1)
        fun_name = ret.group(2)
        
    else:
        print("輸入的命令列格式不正確,請按照一下方式執行:")
        print("python3 py檔案 埠號 框架的檔名:方法名")
        return 

    with open("./web-server.conf") as f:
        conf_info = eval(f.read())
        """
            從配置檔案中讀出來的是字典格式的資料,但不是字典型別
            用 eval( ) 函式將資料轉換為原來的型別,即字典型別
            此時,conf_info 是一個字典型別的變數
            
        """
    
    """定位到模組所在的資料夾"""
    sys.path.append(conf_info['dynamic_path'])

    """ import 可以寫在函式中 """

    frame = __import__(frame_name)  # 該方法返回值標記著匯入的模組物件

    # 在模組物件中根據名稱找到對應的方法
    app = getattr(frame, fun_name)


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


if __name__ == "__main__":
    main()


8、將執行伺服器的命令寫在shell指令碼中




相關文章