python实现基于CGI的Web应用

本文用一个“网上书店”的web应用示例,简要介绍如何用Python实现基于CGI标准的Web应用,介绍python的cgi模块、cigtb模块对编写CGI脚本提供的支持。
 
CGI简介
CGI  Common Gateway Interface (通用网关接口),是一个Internet标准,允许Web服务器运行一个服务器端程序,称为CGI脚本。一般的,CGI脚本都放在一个名为cgi-bin的特殊文件夹内,这样web服务器就知道到哪里查找cgi脚本。
 
CGI Architecture Diagram
When a request arrives, HTTP server execute  a program, and whatever that program outputs is sent back for your browser to display. This function is called the Common Gateway Interface or CGI, and the programs are called CGI scripts. These CGI programs can be a Python Script, PERL Script, Shell Script, C or C++ program etc.
 
 
“网上书店”Web应用目录结构
(操作系统:win7;python版本:3.3)
BookWebApp|
        |cgi-bin
       ------|book_detail_view.py
       ------|book_list_view.py
       ------|template
          ----|yate.py
       ------|mode
          ----|Book.by
       ------|service
          ----|book_service.py
      |resource
      ------- |books.png
      |book.txt
      |index.html
      |run_server.py
 
1、Web服务器
所有的Web应用都要在Web服务器上运行,实际上所有的web服务器都支持CGI,无论是Apache、IIS、nginx、Lighttpd还是其他服务器,它们都支持用python编写的cgi脚本。这些web服务器都比较强大,这里我们使用python自带的简单的web服务器,这个web服务器包含在http.server库模块中。
 
run_server.py:
运行此程序,即启动此web应用。
from http.server import HTTPServer, CGIHTTPRequestHandler

port = 8081

httpd = HTTPServer(('', port), CGIHTTPRequestHandler)
print("Starting simple_httpd on port: " + str(httpd.server_port))
httpd.serve_forever()

2、index.html

首页;URL: “http://localhost:8081/cgi-bin/book_list_view.py” 将调用 cgi-bin文件夹下的book_list_view.py

<html>
<head>
<title>BookStore</title>
</head>
<body>
<h1>Welcome to My Book Store.</h1>
<img src="resource/books.png">
<h3>
please choose your favorite book, click <a href="cgi-bin/book_list_view.py">here</a>.
</h3>
<p>
<strong> Enjoy!</strong>
</p>
</body>
</html>

3、book_list_view.py

图书清单页面。用户选择要查看的图书,提交表单,然后调动图书详细界面。

#Python标准库中定义的CGI跟踪模块:cgibt
import cgitb
cgitb.enable()
#启用这个模块时,会在web浏览器上显示详细的错误信息。enable()函数打开CGI跟踪
#CGI脚本产生一个异常时,Python会将消息显示在stderr(标准输出)上。CGI机制会忽略这个输出,因为它想要的只是CGI的标准输出(stdout)

import template.yate as yate
import service.book_service as book_service

#CGI标准指出,服务器端程序(CGI脚本)生成的任何输出都将会由Web服务器捕获,并发送到等待的web浏览器。具体来说,会捕获发送到Stdout(标准输出)的所有内容

#
一个CGI脚本由2部分组成, 第一部分输出 Response Headers, 第二部分输出常规的html.
print("Content-type:text/html\n")#Response Headers
#网页内容:有html标签组成的文本
print('<html>')
print('<head>')
print('<title>Book List</title>')
print('</head>')
print('<body>')
print('<h2>Book List:</h2>')
print(yate.start_form('book_detail_view.py'))
book_dict=book_service.get_book_dict()
for book_name in book_dict:
    print(yate.radio_button('bookname',book_dict[book_name].name))
print(yate.end_form('detail'))
print(yate.link("/index.html",'Home'))
print('</body>')
print('</html>')

4、yate.py

自定义的简单模板,用于快捷生成html

def start_form(the_url, form_type="POST"):
    return('<form action="' + the_url + '" method="' + form_type + '">')

def end_form(submit_msg="Submit"):
    return('<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>') 

def link(the_link,value):
    link_string = '<a href="' + the_link + '">' + value + '</a>'
    return(link_string)

5、book_detail_view.py

图书详细页面

import cgitb
cgitb.enable()

import cgi
import template.yate as yate
import service.book_service as book_service
import template.yate as yate

#使用cig.FieldStorage() 访问web请求发送给web服务器的数据,这些数据为一个Python字典
form_data = cgi.FieldStorage()

print("Content-type:text/html\n")
print('<html>')
print('<head>')
print('<title>Book List</title>')
print('</head>')
print('<body>')
print(yate.header('Book Detail:'))
try:
   book_name = form_data['bookname'].value
   book_dict=book_service.get_book_dict()
   book=book_dict[book_name]
   print(book.get_html)
except KeyError as kerr:
   print(yate.para('please choose a book...'))
print(yate.link("/index.html",'Home'))
print(yate.link("/cgi-bin/book_list_view.py",'Book List'))
print('</body>')
print('</html>')

6、Book.py

图书类

from template import yate

class Book:
    def __init__(self,name,author,price):
        self.name=name
        self.author=author
        self.price=price
    
    @property
    def get_html(self):
        html_str=''
        html_str+=yate.header('BookName:',4)+yate.para(self.name)
        html_str+=yate.header('Author:',4)+yate.para(self.author)
        html_str+=yate.header('Price:',4)+yate.para(self.price)
        return(html_str)

7、book_service.py

图书业务逻辑类

from model.Book import Book

def get_book_dict():
    book_dict={}
    try:
        with open('book.txt','r') as book_file:
            for each_line in book_file:
                book=parse(each_line)
                book_dict[book.name]=book
    except IOError as ioerr:
        print("IOErr:",ioerr)
    return(book_dict)
    

def parse(book_info):
    (name,author,price)=book_info.split(';')
    book=Book(name,author,price)
    return(book)

8、book.txt

待显示的图书信息(书名;作者;价格)

The Linux Programming Interface: A Linux and UNIX System Prog;Michael Kerrisk;$123.01
HTML5 and CSS3, Illustrated Complete (Illustrated Series);Jonathan Meersman Sasha Vodnik;$32.23
Understanding the Linux Kernel;Daniel P. Bovet Marco Cesati;$45.88
Getting Real;Jason Fried, Heinemeier David Hansson, Matthew Linderman;$87.99

 
测试结果
运行run_server.py,浏览器访问:http://localhost:8081/
控制台会监控请求的信息:
 
点击“here”,查看图书清单,即书名列表
 
选择书名,点击“detail”提交表单,返回该书的详细信息:书名、作者、价格
 
 
 (转载请注明出处 ^.^)
 
posted @ 2013-06-24 22:51  windlaughing  阅读(10974)  评论(8编辑  收藏  举报