在拖了一周之后,今天终于在一个小时之内将一个迷你的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()