在拖了一周之后,今天終於在一個小時之內將一個迷你的Web寫出來了,最近改其它項目的bug頭好大,但是好喜歡這樣的狀態。
黑色的12月,所有的任務都聚集在了12月,然后期末考試也顧不上好好復習了,但是但是,我要一步步的把手上的項目做出來!!!
回歸正題了:這次的Python網絡編程也是速成的,對於Python只是看了大體的語言框架后就直接上手寫網絡編程部分了,有錯希望前輩指正~~
Python版本:2.7
IDE:PyCharm
接着前面幾篇基礎的這次主要修改了下面的部分:
最終完成的是下面的結構:
一、響應靜態頁面
所以這一步就該處理靜態頁面了,處理靜態頁面就是根據請求的頁面名得到磁
盤上的頁面文件並返回。
在當前目錄下創建新文件 plain.html,這是測試用的靜態頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Plain Page</title> </head> <body> <p>HAha, this is plain page...</p> </body> </html>
在 server.py 中導入需要的庫
import sys, os, BaseHTTPServer
為服務器程序寫一個異常類
class ServerException(Exception): ''' 服務器內部錯誤 ''' pass
重寫 do_GET 函數
# deal with a request def do_GET(self): try: full_path = os.getcwd() + self.path if not os.path.exists(full_path): raise ServerException("'{0}' not found".format(self.path)) elif os.path.isfile(full_path): self.handle_file(full_path) # 訪問根路徑 elif os.path.isdir(full_path): # fullPath = os.path.join(fullPath, "index.html") full_path += "index.html" if os.path.isfile(full_path): self.handle_file(full_path) else: raise ServerException("'{0}' not found".format(self.path)) else: raise ServerException("Unknown object '{0}'".format(self.path)) except Exception as msg: self.handle_error(msg)
首先看完整路徑的代碼, os.getcwd() 是當前的工作目錄, self.path 保存了請求的相對路徑, RequestHandler 是繼承自 BaseHTTPRequestHandler 的,它已經
將請求的相對路徑保存在 self.path 中了。
編寫文件處理函數和錯誤處理函數:
# page model Page = ''' <html> <body> <table border=2s> <tr> <td>Header</td> <td>Value</td> </tr> <tr> <td>Date</td><td> and time</td> <td>{date_time}</td> </tr> <tr> <td>Client host</td> <td>{client_host}</td> </tr> <tr> <td>Client port</td> <td>{client_port}</td> </tr> <tr> <td>Command</td> <td>{command}</td> </tr> <tr> <td>Path</td> <td>{path}</td> </tr> </table> </body> </html> ''' Error_Page = """\ <html> <body> <h1>Error accessing {path}</h1> <p>{msg}</p> </body> </html> """ def handle_error(self, msg): content = self.Error_Page.format(path=self.path, msg=msg) self.send_content(content, 404) def handle_file(self, full_path): # 處理 python 腳本 if full_path.endswith('.py'): # data 為腳本運行后的返回值 data = subprocess.check_output(['python', full_path]) self.send_content(data) return try: with open(full_path, 'rb') as reader: content = reader.read() self.send_content(content) except IOError as msg: msg = "'{0}' cannot be read: {1}".format(self.path, msg) self.handle_error(msg)
看下效果嘍:
再測試一下錯誤的路徑:
上面也是返回了錯誤頁面
但同時如果用 postman 觀察的話,返回的是200 狀態碼,要不要希望它能夠返回 404呢,哈哈,所以還需要修改一
下 handle_error 與 send_content 函數
這次的話如果在輸入不存在的 url 就能返回 404 狀態碼了。
在根 url 顯示首頁內容
在工作目錄下添加 index.html 文件
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>index</title> </head> <body> <h1>Index Page </h1> <p>I love Python, to be honest</p> </body> </html>
此時do_GET函數也要修改下:
def do_GET(self): try: full_path = os.getcwd() + self.path if not os.path.exists(full_path): raise ServerException("'{0}' not found".format(self.path)) elif os.path.isfile(full_path): self.handle_file(full_path) # 訪問根路徑 elif os.path.isdir(full_path): # fullPath = os.path.join(fullPath, "index.html") full_path += "index.html" if os.path.isfile(full_path): self.handle_file(full_path) else: raise ServerException("'{0}' not found".format(self.path)) else: raise ServerException("Unknown object '{0}'".format(self.path)) except Exception as msg: self.handle_error(msg)
看看效果了:
.CGI 協議
有時候呢,大部分人是不希望每次都給服務器加新功能時都要到服務器的源代碼里進行
修改。所以,如果程序能獨立在另一個腳本文件里運行那就再好不過啦。下面實現CGI:
如下面在 html 頁面上顯示當地時間。
創建新文件 time.py
from datetime import datetime print '''\ <html> <body> <p>Generated {0}</p> </body> </html>'''.format(datetime.now())
導入 import subprocess,這是一個在程序中生成子進程的模塊,它是對fork(),exec()等函數做了封裝
【不過目前我還沒有掌握,學長說以后會在操作系統課程中學到~~~】
修改 handleFile()函數
def handle_file(self, full_path): # 處理 python 腳本 if full_path.endswith('.py'): # data 為腳本運行后的返回值 data = subprocess.check_output(['python', full_path]) self.send_content(data) return try: with open(full_path, 'rb') as reader: content = reader.read() self.send_content(content) except IOError as msg: msg = "'{0}' cannot be read: {1}".format(self.path, msg) self.handle_error(msg)
看看效果了:
w(゚Д゚)w。
它成功的顯示了現在的時間,看到后不禁要擔心過會兒一定不能再被教學區的阿姨趕出自習室了。。
不不,又沒有去跑步啊。。。
上面的功能都是在學長寫的文檔的指導下做了自己的修改一行一行實現的~~
下篇還會在此基礎上對BaseHTTPServer.HTTPServer,
BaseHTTPServer.BaseHTTPRequestHandler 再學習去實現, 現在只是實現了最基
礎的部分。
后面完整的代碼如下:
# -*-coding:utf-8 -*- import BaseHTTPServer import os import subprocess class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): """dealt with and return page""" # page model Page = ''' <html> <body> <table border=2s> <tr> <td>Header</td> <td>Value</td> </tr> <tr> <td>Date</td><td> and time</td> <td>{date_time}</td> </tr> <tr> <td>Client host</td> <td>{client_host}</td> </tr> <tr> <td>Client port</td> <td>{client_port}</td> </tr> <tr> <td>Command</td> <td>{command}</td> </tr> <tr> <td>Path</td> <td>{path}</td> </tr> </table> </body> </html> ''' Error_Page = """\ <html> <body> <h1>Error accessing {path}</h1> <p>{msg}</p> </body> </html> """ def handle_error(self, msg): content = self.Error_Page.format(path=self.path, msg=msg) self.send_content(content, 404) def handle_file(self, full_path): # 處理 python 腳本 if full_path.endswith('.py'): # data 為腳本運行后的返回值 data = subprocess.check_output(['python', full_path]) self.send_content(data) return try: with open(full_path, 'rb') as reader: content = reader.read() self.send_content(content) except IOError as msg: msg = "'{0}' cannot be read: {1}".format(self.path, msg) self.handle_error(msg) # deal with a request def do_GET(self): try: full_path = os.getcwd() + self.path if not os.path.exists(full_path): raise ServerException("'{0}' not found".format(self.path)) elif os.path.isfile(full_path): self.handle_file(full_path) # 訪問根路徑 elif os.path.isdir(full_path): # fullPath = os.path.join(fullPath, "index.html") full_path += "index.html" if os.path.isfile(full_path): self.handle_file(full_path) else: raise ServerException("'{0}' not found".format(self.path)) else: raise ServerException("Unknown object '{0}'".format(self.path)) except Exception as msg: self.handle_error(msg) @property def create_page(self): values = { 'date_time': self.date_time_string(), 'client_host': self.client_address[0], 'client_port': self.client_address[1], 'command': self.command, 'path': self.path } page = self.Page.format(**values) return page pass def send_content(self, content, status=200): self.send_response(status) self.send_header("Content-type", "text/html") self.send_header("Content-Length", str(len(content))) self.end_headers() self.wfile.write(content) pass class ServerException(Exception): pass if __name__ == '__main__': server_address = ('127.0.0.1', 5555) server = BaseHTTPServer.HTTPServer(server_address, RequestHandler) server.serve_forever()