miniweb框架

WSGI接口

而通常web服務(wù)器必須具備WSGI接口,所有的現(xiàn)代Python Web框架都已具備WSGI接口,它讓你不對(duì)代碼作修改就能使服務(wù)器和特點(diǎn)的web框架協(xié)同工作。

WSGI由web服務(wù)器支持,而web框架允許你選擇適合自己的配對(duì),但它同樣對(duì)于服務(wù)器和框架開(kāi)發(fā)者提供便利使他們可以專(zhuān)注于自己偏愛(ài)的領(lǐng)域和專(zhuān)長(zhǎng)而不至于相互牽制。其他語(yǔ)言也有類(lèi)似接口:java有Servlet API,Ruby 有 Rack。

我們需在web框架內(nèi)定義一個(gè)WSGI接口函數(shù),而web服務(wù)器與框架進(jìn)行協(xié)同工作時(shí),只需通過(guò)該接口函數(shù)即可,其他的實(shí)現(xiàn)均封裝在該框架的底部;該接口函數(shù)如下:

# WSGI接口函數(shù)
def application(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    return 'Hello World!'

參數(shù):

environ :一個(gè)包含所有HTTP請(qǐng)求信息的dict對(duì)象;即告訴框架需要調(diào)取哪些動(dòng)態(tài)資源頁(yè)面;

start_response :一個(gè)發(fā)送HTTP響應(yīng)的函數(shù)。該函數(shù)相當(dāng)于HTTP服務(wù)器給與框架的一個(gè)容器,而框架將頭部信息通過(guò)參數(shù)的方式存放在該函數(shù)內(nèi),最后服務(wù)器再?gòu)闹腥〕觯?/p>

返回值:

返回的是請(qǐng)求的動(dòng)態(tài)資源的響應(yīng)消息的body部分;通常web框架將動(dòng)態(tài)資源替換HTML模板的消息返回給服務(wù)器;


代碼實(shí)例

web_server.py

解析:

1、在web服務(wù)器中,我們首先根據(jù)資源的請(qǐng)求不同,給出的處理方式不同即靜態(tài)資源直接從本地磁盤(pán)讀取,而動(dòng)態(tài)資源交給web框架處理。

2、在與web框架進(jìn)行交互,調(diào)用了wsgi接口函數(shù),傳入了字典env,用來(lái)告訴服務(wù)器需求頁(yè)面;以及函數(shù)set_response_header用來(lái)獲取web框架傳遞的頭部信息;并且將返回值作為body;整個(gè)與web框架的交互均是圍繞著wsgi接口函數(shù)來(lái)實(shí)現(xiàn)的;

3、隨后便是讓我們的服務(wù)器運(yùn)行可以指定端口和web框架來(lái)運(yùn)行,則定義了main函數(shù)下面的一些代碼,通過(guò)sys.argv來(lái)獲取到指定的端口和web_frame:application等,再將這些作為參數(shù)傳入類(lèi) WSGIServer中來(lái)執(zhí)行;

import socket
import re
import multiprocessing
import time
# import dynamic.mini_frame
import sys
 
 
class WSGIServer(object):
    def __init__(self, port, app, static_path):
        # 1. 創(chuàng)建套接字
        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. 變?yōu)楸O(jiān)聽(tīng)套接字
        self.tcp_server_socket.listen(128)
 
        self.application = app
        self.static_path = static_path
 
    def service_client(self, new_socket):
        """為這個(gè)客戶(hù)端返回?cái)?shù)據(jù)"""
 
        # 1. 接收瀏覽器發(fā)送過(guò)來(lái)的請(qǐng)求 ,即http請(qǐng)求 
        # GET / HTTP/1.1
        # .....
        request = new_socket.recv(1024).decode("utf-8")
        # print(">>>"*50)
        # print(request)
 
        request_lines = request.splitlines()
        print("")
        print(">"*20)
        print(request_lines)
 
        # 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("*"*50, file_name)
            if file_name == "/":
                file_name = "/index.html"
 
        # 2. 返回http格式的數(shù)據(jù),給瀏覽器
        # 2.1 如果請(qǐng)求的資源不是以.html結(jié)尾,那么就認(rèn)為是靜態(tài)資源(css/js/png,jpg等)
        if not file_name.endswith(".html"):
            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()
                # 2.1 準(zhǔn)備發(fā)送給瀏覽器的數(shù)據(jù)---header
                response = "HTTP/1.1 200 OK\r\n"
                response += "\r\n"
                # 2.2 準(zhǔn)備發(fā)送給瀏覽器的數(shù)據(jù)---boy
                # response += "hahahhah"
 
                # 將response header發(fā)送給瀏覽器
                new_socket.send(response.encode("utf-8"))
                # 將response ic.mini_frame.applicationbody發(fā)送給瀏覽器
                new_socket.send(html_content)
        else:
            # 2.2 如果是以.py結(jié)尾,那么就認(rèn)為是動(dòng)態(tài)資源的請(qǐng)求
 
            env = dict()  # 這個(gè)字典中存放的是web服務(wù)器要傳遞給 web框架的數(shù)據(jù)信息
            env['PATH_INFO'] = file_name
            # {"PATH_INFO": "/index.py"}
            # body = dynamic.mini_frame.application(env, self.set_response_header)
            body = self.application(env, 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
            # 發(fā)送response給瀏覽器
            new_socket.send(response.encode("utf-8"))
 
 
        # 關(guān)閉套接
        new_socket.close()
 
    def set_response_header(self, status, headers):
        self.status = status
        self.headers = [("server", "mini_web v8.8")]
        self.headers += headers
       
 
    def run_forever(self):
        """用來(lái)完成整體的控制"""
 
        while True:
            # 4. 等待新客戶(hù)端的鏈接
            new_socket, client_addr = self.tcp_server_socket.accept()
 
            # 5. 為這個(gè)客戶(hù)端服務(wù)
            p = multiprocessing.Process(target=self.service_client, args=(new_socket,))
            p.start()
 
            new_socket.close()
 
 
        # 關(guān)閉監(jiān)聽(tīng)套接字
        self.tcp_server_socket.close()
 
 
def main():
    """控制整體,創(chuàng)建一個(gè)web 服務(wù)器對(duì)象,然后調(diào)用這個(gè)對(duì)象的run_forever方法運(yùn)行"""
    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("端口輸入錯(cuò)誤。。。。。")
            return
    else:
        print("請(qǐng)按照以下方式運(yùn)行:")
        print("python3 xxxx.py 7890 mini_frame:application")
        return
   
    # mini_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("請(qǐng)按照以下方式運(yùn)行:")
        print("python3 xxxx.py 7890 mini_frame:application")
        return
 
    with open("./web_server.conf") as f:
        conf_info = eval(f.read())
 
    # 此時(shí) conf_info是一個(gè)字典里面的數(shù)據(jù)為:
    # {
    #     "static_path":"./static",
    #     "dynamic_path":"./dynamic"
    # }
 
 
    sys.path.append(conf_info['dynamic_path'])
 
    # import frame_name --->找frame_name.py
    frame = __import__(frame_name)  # 返回值標(biāo)記這 導(dǎo)入的這個(gè)模板
    app = getattr(frame, app_name)  # 此時(shí)app就指向了 dynamic/mini_frame模塊中的application這個(gè)函數(shù)
 
    # print(app)
 
    wsgi_server = WSGIServer(port, app, conf_info['static_path'])
    wsgi_server.run_forever()
 
 
if __name__ == "__main__":
    main()

dynamic/mini_frame.py
 在該框架內(nèi):定義了wsgi接口函數(shù),以及獲取指定頁(yè)面的函數(shù),在下篇中我們將會(huì)介紹如何將數(shù)據(jù)庫(kù)的內(nèi)容導(dǎo)入;

import re
import urllib.parse
import logging
from pymysql import connect

"""
URL_FUNC_DICT = {
   "/index.html": index,
   "/center.html": center
}
"""

URL_FUNC_DICT = dict()


def route(url):
   def set_func(func):
       # URL_FUNC_DICT["/index.py"] = index
       URL_FUNC_DICT[url] = func
       def call_func(*args, **kwargs):
           return func(*args, **kwargs)
       return call_func
   return set_func


@route(r"/index.html")
def index(ret):
   with open("./templates/index.html") as f:
       content = f.read()

   # my_stock_info = "哈哈哈哈 這是你的本月名稱(chēng)....."
   # content = re.sub(r"\{%content%\}", my_stock_info, content)
   # 創(chuàng)建Connection連接
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   # 獲得Cursor對(duì)象
   cs = conn.cursor()
   cs.execute("select * from info;")
   stock_infos = cs.fetchall()
   cs.close()
   conn.close()

   tr_template = """<tr>
       <td>%s</td><td>%s</td> <td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>
           <input type="button" value="添加" id="toAdd" name="toAdd" systemidvaule="%s">
       </td>
       </tr>
   """

   html = ""
   for line_info in stock_infos:
       html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6],line_info[7], line_info[1])

   # content = re.sub(r"\{%content%\}", str(stock_infos), content)
   content = re.sub(r"\{%content%\}", html, content)

   return content
   

@route(r"/center.html")
def center(ret):
   with open("./templates/center.html") as f:
       content = f.read()

   # my_stock_info = "這里是從mysql查詢(xún)出來(lái)的數(shù)據(jù)。。。"
   # content = re.sub(r"\{%content%\}", my_stock_info, content)
   # 創(chuàng)建Connection連接
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   # 獲得Cursor對(duì)象
   cs = conn.cursor()
   cs.execute("select i.code,i.short,i.chg,i.turnover,i.price,i.highs,f.note_info from info as i inner join focus as f on i.id=f.info_id;")
   stock_infos = cs.fetchall()
   cs.close()
   conn.close()

   tr_template = """
       <tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td>
           <td>
               <a type="button" class="btn btn-default btn-xs" href="/update/%s.html"> <span class="glyphicon glyphicon-star" aria-hidden="true"></span> 修改 </a>
           </td>
           <td>
               <input type="button" value="刪除" id="toDel" name="toDel" systemidvaule="%s">
           </td>
       </tr>
   """

   html = ""
   for line_info in stock_infos:
       html += tr_template % (line_info[0],line_info[1],line_info[2],line_info[3],line_info[4],line_info[5],line_info[6], line_info[0], line_info[0])

   # content = re.sub(r"\{%content%\}", str(stock_infos), content)
   content = re.sub(r"\{%content%\}", html, content)

   return content

# 給路由添加正則表達(dá)式的原因:在實(shí)際開(kāi)發(fā)時(shí),url中往往會(huì)帶有很多參數(shù),例如/add/000007.html中000007就是參數(shù),
# 如果沒(méi)有正則的話(huà),那么就需要編寫(xiě)N次@route來(lái)進(jìn)行添加 url對(duì)應(yīng)的函數(shù) 到字典中,此時(shí)字典中的鍵值對(duì)有N個(gè),浪費(fèi)空間
# 而采用了正則的話(huà),那么只要編寫(xiě)1次@route就可以完成多個(gè) url例如/add/00007.html /add/000036.html等對(duì)應(yīng)同一個(gè)函數(shù),此時(shí)字典中的鍵值對(duì)個(gè)數(shù)會(huì)少很多
@route(r"/add/(\d+)\.html")
def add_focus(ret):

   # 1. 獲取股票代碼
   stock_code = ret.group(1)

   # 2. 判斷試下是否有這個(gè)股票代碼
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   cs = conn.cursor()
   sql = """select * from info where code=%s;"""
   cs.execute(sql, (stock_code,))
   # 如果要是沒(méi)有這個(gè)股票代碼,那么就認(rèn)為是非法的請(qǐng)求
   if not cs.fetchone():
       cs.close()
       conn.close()
       return "沒(méi)有這支股票,大哥 ,我們是創(chuàng)業(yè)公司,請(qǐng)手下留情..."

   # 3. 判斷以下是否已經(jīng)關(guān)注過(guò)
   sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
   cs.execute(sql, (stock_code,))
   # 如果查出來(lái)了,那么表示已經(jīng)關(guān)注過(guò)
   if cs.fetchone():
       cs.close()
       conn.close()
       return "已經(jīng)關(guān)注過(guò)了,請(qǐng)勿重復(fù)關(guān)注..."

   # 4. 添加關(guān)注
   sql = """insert into focus (info_id) select id from info where code=%s;"""
   cs.execute(sql, (stock_code,))
   conn.commit()
   cs.close()
   conn.close()

   return "關(guān)注成功...."


@route(r"/del/(\d+)\.html")
def del_focus(ret):

   # 1. 獲取股票代碼
   stock_code = ret.group(1)

   # 2. 判斷試下是否有這個(gè)股票代碼
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   cs = conn.cursor()
   sql = """select * from info where code=%s;"""
   cs.execute(sql, (stock_code,))
   # 如果要是沒(méi)有這個(gè)股票代碼,那么就認(rèn)為是非法的請(qǐng)求
   if not cs.fetchone():
       cs.close()
       conn.close()
       return "沒(méi)有這支股票,大哥 ,我們是創(chuàng)業(yè)公司,請(qǐng)手下留情..."

   # 3. 判斷以下是否已經(jīng)關(guān)注過(guò)
   sql = """ select * from info as i inner join focus as f on i.id=f.info_id where i.code=%s;"""
   cs.execute(sql, (stock_code,))
   # 如果沒(méi)有關(guān)注過(guò),那么表示非法的請(qǐng)求
   if not cs.fetchone():
       cs.close()
       conn.close()
       return "%s 之前未關(guān)注,請(qǐng)勿取消關(guān)注..." % stock_code

   # 4. 取消關(guān)注
   # sql = """insert into focus (info_id) select id from info where code=%s;"""
   sql = """delete from focus where info_id = (select id from info where code=%s);"""
   cs.execute(sql, (stock_code,))
   conn.commit()
   cs.close()
   conn.close()

   return "取消關(guān)注成功...."


@route(r"/update/(\d+)\.html")
def show_update_page(ret):
   """顯示修改的那個(gè)頁(yè)面"""
   # 1. 獲取股票代碼
   stock_code = ret.group(1)

   # 2. 打開(kāi)模板
   with open("./templates/update.html") as f:
       content = f.read()

   # 3. 根據(jù)股票代碼查詢(xún)相關(guān)的備注信息
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   cs = conn.cursor()
   sql = """select f.note_info from focus as f inner join info as i on i.id=f.info_id where i.code=%s;"""
   cs.execute(sql, (stock_code,))
   stock_infos = cs.fetchone()
   note_info = stock_infos[0]  # 獲取這個(gè)股票對(duì)應(yīng)的備注信息
   cs.close()
   conn.close()

   content = re.sub(r"\{%note_info%\}", note_info, content)
   content = re.sub(r"\{%code%\}", stock_code, content)

   return content


@route(r"/update/(\d+)/(.*)\.html")
def save_update_page(ret):
   """"保存修改的信息"""
   stock_code = ret.group(1)
   comment = ret.group(2)
   comment = urllib.parse.unquote(comment)
  
   conn = connect(host='localhost',port=3306,user='root',password='mysql',database='stock_db',charset='utf8')
   cs = conn.cursor()
   sql = """update focus set note_info=%s where info_id = (select id from info where code=%s);"""
   cs.execute(sql, (comment, stock_code))
   conn.commit()
   cs.close()
   conn.close()

   return "修改成功..."


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

   """
   if file_name == "/index.py":
       return index()
   elif file_name == "/center.py":
       return center()
   else:
       return 'Hello World! 我愛(ài)你中國(guó)....'
   """

   logging.basicConfig(level=logging.INFO, 
                       filename='./log.txt', 
                       filemode='a', 
                       format='%(asctime)s - %(filename)s[line:%(lineno)d] - %(levelname)s: %(message)s') 

   logging.info("訪問(wèn)的是,%s" % file_name)

   try:
       # func = URL_FUNC_DICT[file_name]
       # return func()
       # return URL_FUNC_DICT[file_name]()
       for url, func in URL_FUNC_DICT.items():
           # {
           #   r"/index.html":index,
           #   r"/center.html":center,
           #   r"/add/\d+\.html":add_focus
           # }
           ret = re.match(url, file_name)
           if ret:
               return func(ret)
       else:
           logging.warning("沒(méi)有對(duì)應(yīng)的函數(shù)....")
           return "請(qǐng)求的url(%s)沒(méi)有對(duì)應(yīng)的函數(shù)...." % file_name


   except Exception as ret:
       return "產(chǎn)生了異常:%s" % str(ret)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容