1、Web应用的5个步骤
①用户在他所选择的Web浏览器中输入一个Web地址、选择一个超链接,或者点击一个按钮。
②Web浏览器将用户的动作转换为一个Web请求,并通过互联网把它发送到一个服务器。
③Web服务器收到Web请求,必须决定接下来做什么。
④Web服务器处理Web请求,创建一个Web响应,这会通过互联网发回给等待着的Web浏览器。
⑤第④步(可能)有很多子步骤
实际上第4步,可能包括很多子步骤,这要看Web服务器如何生成响应。如果服务器所要做的只是找到静态内容并把它发回给浏览器,那么这些子步骤不会太费劲,因为这些都只是文件I/O而已。
不过,如果必须生成动态内容,就要包括以下子步骤:Web服务器要找到所要执行的程序、执行找到的程序,然后捕获程序的输出作为Web响应……再把它发回给还在等待的Web浏览器。早在Web发展的初期,这个生成动态内容的过程已经得到标准化,称为通用网关接口(Common Gateway Interface,CGI)。符合标准的程序通常称为CGI脚本。
2、采用MVC设计Web应用
好的Web应用应当遵循模型-视图-控制器(Model-View-Controller)模式,这有助于将Web应用的代码分解为易于管理的功能模块(或组件):
模型:存储(以及有时处理)Web应用数据的代码。
视图:格式化和显示Web应用用户界面的代码。
控制器:将Web应用“粘合”在一起并提供业务逻辑的代码。
通过遵循MVC模式,可以合理构建你的Web应用,从而当出现新的需求时能够轻松扩展。另外还可以将工作分给多个人共同完成,每个人分别负责一个组件。
3、为数据建模
Web服务器需要存储数据的一个副本,在这里数据就是Kelly教练的计时值。启动这个Web应用时,需要把文本文件中的数据转换为AthleteList对象实例,存储在一个字典中(按选手名索引),然后保存为一个pickle文件。下面把这个功能放在一个名为put _ to _ store()的新函数中。在Web应用运行时,pickle文件中的数据可以作为一个字典应用使用。下面把这个功能放在另一个名为get _ from _store()的函数中。
class AthleteList(list):
def __init__(self,a_name,a_dob=None,a_times=[]):
list.__init__([])
self.name=a_name
self.dob=a_dob
self.extend(a_times)
def top3(self):
return(sorted(set([santize(each_time)for each_time in self]))[0:3])
def get_coach_data(filename):
try:
with open(filename) as f:
data=f.readline()
temp1=data.strip().split(',')
return AthleteList(temp1.pop(0),temp1.pop(0),temp1)
except IOError as err:
print('File error:'+str(err))
return(None)
def santize(time_item):
if '-' in time_item:
splits='-'
elif ':' in time_item:
splits=':'
else:
return time_item
(mins,secs)=time_item.split(splits)
return(mins+'.'+secs)
import pickle
from athletelist import AthleteList
def get_coach_data(filename):
try:
with open(filename) as f:
data=f.readline()
temp1=data.strip().split(',')
return AthleteList(temp1.pop(0),temp1.pop(0),temp1)
except IOError as err:
print('File error:'+str(err))
return(None)
def put_to_store(files_list):
all_athletes={}
for each_file in files_list:
ath=get_coach_data(each_file)
all_athletes[ath.name]=ath
try:
with open('athletes.pickle','wb') as athf:
pickle.dump(all_athletes,athf)
except IOError as ioerr:
print('File error(put_to_store):'+str(ioerr))
return (all_athletes)
def get_from_store():
all_athletes={}
try:
with open('athletes.pickle','rb') as athf:
all_athletes=pickle.load(athf)
except IOError as ioerr:
print('File error(get_from_store):'+str(ioerr))
return (all_athletes)
'''下面来测试代码,确保它能满足要求,F5执行,以下是在IDLE窗口键入的'''
>>> the_files=['sarah2.txt','james2.txt','mikey2.txt','julie2.txt']
>>> data=put_to_store(the_files)
>>> data
{'Sarah Sweeney': ['2:58', '2.58', '2:39', '2-25', '2-55', '2:54', '2.18', '2:55', '2:55', '2:22', '2-21', '2.22'], 'James Lee': ['2-34', '3:21', '2.34', '2.45', '3.01', '2:01', '2:01', '3:10', '2-22', '2-01', '2.01', '2:16'], 'Mikey McManus': ['2:22', '3.01', '3:01', '3.02', '3:02', '3.02', '3:22', '2.49', '2:38', '2:40', '2.22', '2-31'], 'Julie Jones': ['2.59', '2.11', '2:11', '2:23', '3-10', '2-23', '3:10', '3.21', '3-21', '3.01', '3.02', '2:59']}
>>> for each_athlete in data:
print(data[each_athlete].name+' '+data[each_athlete].dob)
Sarah Sweeney 2002-6-17
James Lee 2002-3-14
Mikey McManus 2002-2-24
Julie Jones 2002-8-17
>>> data_copy=get_from_store()
>>> for each_data in data_copy:
print(data_copy[each_data].name+' '+data_copy[each_data].dob)
Sarah Sweeney 2002-6-17
James Lee 2002-3-14
Mikey McManus 2002-2-24
Julie Jones 2002-8-17
4、查看界面
上面已经编写了模型代码,下面来看看视图代码,这会创建Web应用的用户界面。在Web上,用户界面用Web的标记技术HTML来创建。
YATE另外一个模板引擎
from string import Template
def start_response(resp="text/html"):
return('Content-type: ' + resp + '\n\n')
def include_header(the_title):
with open('templates/header.html') as headf:
head_text = headf.read()
header = Template(head_text)
return(header.substitute(title=the_title))
def include_footer(the_links):
with open('templates/footer.html') as footf:
foot_text = footf.read()
link_string = ''
for key in the_links:
link_string += '<a href="' + the_links[key] + '">' + key + '</a> '
footer = Template(foot_text)
return(footer.substitute(links=link_string))
def start_form(the_url, form_type="POST"):
return('<form action="' + the_url + '" method="' + form_type + '">')
def end_form(submit_msg="Submit"):
return('<p></p><input type=submit value="' + submit_msg + '"></form>')
def radio_button(rb_name, rb_value):
return('<input type="radio" name="' + rb_name +
'" value="' + rb_value + '"> ' + rb_value + '<br />')
def u_list(items):
u_string = '<ul>'
for item in items:
u_string += '<li>' + item + '</li>'
u_string += '</ul>'
return(u_string)
def header(header_text, header_level=2):
return('<h' + str(header_level) + '>' + header_text +
'</h' + str(header_level) + '>')
def para(para_text):
return('<p>' + para_text + '</p>')
5、控制你的代码
模型代码已经准备好,而且用yate模块帮助建立视图代码,现在该用控制器代码把它们粘合在一起了。
首要的事情是你要合理的安排Web代码的目录结构,保证它有条理有组织,这样有利于以后增强扩展Web应用的能力。
6、CGI让Web服务器运行程序
通用网关接口(CGI)是一个Internet标准,允许Web服务器运行一个服务器端程序,称为CGI脚本。一般的CGI脚本放在cgi-bin的特殊文件夹下,这样Web服务器才能知道哪里找到CGI脚本,有些操作系统中(主要是类UNIX的系统),CGI脚本必须先设置为可执行,Web服务器才能执行这些脚本对Web请求做出响应。
实际上这个世界上的所有Web服务器都支持CGI脚本,但在这里使用有些大材小用了,好在,Python提供了它自己的Web服务器,这个Web服务器包含在http.server库模块中。请查看webapp.zip下载包的内容:它提供了一个支持CGI的Web服务器,名为simple_httpd.py
'''用python构建web服务器必须有这5行代码'''
from http.server import HTTPServer, CGIHTTPRequestHandler
'''导入http服务器和CGI模块'''
port = 8080
'''指定一个端口'''
httpd = HTTPServer(('', port), CGIHTTPRequestHandler)
'''创建http服务器'''
print("Starting simple_httpd on port: " + str(httpd.server_port))
httpd.serve_forever()
'''显示友好的消息并启动服务器'''
7、显示选手列表
创建一个名为generate_list.py的程序
import athletemodel
import yate
import glob
'''利用glob可以向操作系统查询一个文件名列表'''
data_files=glob.glob("data/*.txt")
athletes=athletemodel.put_to_store(data_files)
print(yate.start_response())
print(yate.include_header("Coach Kelly's List of Athletes"))
print(yate.start_form("generate_timing_data.py"))
print(yate.para("Selecct an athlete from the list to work with:"))
for each_athlete in athletes:
print(yate.radio_button("which_athlete",athletes[each_athlete].name))
print(yate.end_form("Select"))
print(yate.include_footer({"Home":"/index.html"}))
8、创建另一个CGI脚本generate _ timing_data.py
生成一个新的HTML页面,其中包含所选选手的前3个时间。
import athletemodel
import yate
import cgi
import cgitb
cgitb.enable()
athletes=athletemodel.get_from_store()
form_data=cgi.FieldStorage()
'''获取所有表单数据,并放在一个字典中'''
athlete_name=form_data['which_athlete'].value
print(yate.start_response())
print(yate.include_header("Coach Kelly's Timing Data"))
print(yate.header("Athlete:"+athlete_name+",DOB:"+athletes[athlete_name].dob+"."))
print(yate.para("The top times for this athlete are:"))
print(yate.u_list(athletes[athlete_name].top3))
print(yate.include_footer({"Home":"/index.html","select another athlete":"generate_list.py"}))
9、测试驱动
为了测试CGI脚本,需要启动一个Web服务器并运行。
从webapp文件夹打开一个命令窗口,启动你的web服务器:
python.exe simplehttpd.py
然后在浏览器上访问:http://127.0.0.1:8080
10、启用CGI跟踪来帮助解决错误
在CGI脚本前面增加这两行代码启用python的cgi跟踪技术:
import cgitb
cgitb.enable()
注:
如果一个方法使用了@property修饰符,那么这个方法会被看成是一个类属性,所以访问的时候不必加括号了