自己動手寫一個web框架,因為我是菜鳥,對於python的一些內建函數不是清楚,所以在寫這篇文章之前需要一些python和WSGI的預備知識,這是一系列文章。這一篇只實現了如何處理url。
參考這篇文章:http://www.cnblogs.com/russellluo/p/3338616.html
預備知識
web框架主要是實現web服務器和web應用之間的交互。底層的網絡協議主要有web服務器完成。譬如監聽端口,填充報文等等。
Python內建函數__iter__和__call__和WSGI
迭代器iterator
迭代器為類序列對象提供了類序列的接口,也就是說類序列對象可以通過迭代器像序列一樣進行迭代。說簡單一點就是遍歷對象。如果想讓類是可迭代的,那么就必須實現__iter__和next()。
__call__
只要在類定義的時候實現了__call__方法,那么該類的對象就是可調有的,即可以將對象當做函數來使用。這里只用明白什么是__call__函數即可,因為WSGI規范中用要求。
WSGI
關於WSGI的介紹可以點擊http://webpython.codepoint.net,有很詳細的介紹。這里只說明一下大概。WSGI接口是用可調用的對象實現的:一個函數,一個方法或者一個可調用的實例。下面是一個實例,注釋寫的很詳細:
# This is our application object. It could have any name,
# except when using mod_wsgi where it must be "application"
def application( # It accepts two arguments:
# environ points to a dictionary containing CGI like environment variables
# which is filled by the server for each received request from the client
environ,
# start_response is a callback function supplied by the server
# which will be used to send the HTTP status and headers to the server
start_response):
# build the response body possibly using the environ dictionary
response_body = 'The request method was %s' % environ['REQUEST_METHOD']
# HTTP response code and message
status = '200 OK'
# These are HTTP headers expected by the client.
# They must be wrapped as a list of tupled pairs:
# [(Header name, Header value)].
response_headers = [('Content-Type', 'text/plain'),
('Content-Length', str(len(response_body)))]
# Send them to the server using the supplied function
start_response(status, response_headers)
# Return the response body.
# Notice it is wrapped in a list although it could be any iterable.
return [response_body]
簡單來說就是根據接收的參數來返回相應的結果。
設計web框架
我之前用過django寫過一個很簡單的博客,目前放在SAE上,好久沒更新了。網址:http://3.mrzysv5.sinaapp.com。一個web框架最基本的要求就是簡化用戶的代碼量。所以在django中,我只需要寫view、model和url配置文件。下面是我用django時寫的一個處理視圖的函數:
def blog_list(request):
blogs = Article.objects.all().order_by('-publish_date')
blog_num = Article.objects.count()
return render_to_response('index.html', {"blogs": blogs,"blog_num":blog_num}, context_instance=RequestContext(request))
def blog_detail(request):
bid = request.GET.get('id','')
blog = Article.objects.get(id=bid)
return render_to_response('blog.html',{'blog':blog})
需要我完成的就是操作數據庫,返回相應的資源。所以我要編寫的web框架就要盡可能的封裝一些底層操作,留給用戶一些可用的接口。根據我的觀察,web框架的處理過程大致如下:
- 一個WSGI應用的基類初始化時傳入配置好的url文件
- 用戶寫好處理方法,基類根據url調用方法
- 返回給客戶端視圖
一個WSGI基類,主要有以下的功能:
- 處理environ參數
- 根據url得到方法或者類名,並執行后返回
import re
class WSGIapp:
headers = []
def __init__(self,urls=()):
self.urls = urls
self.status = '200 OK'
def __call__(self,environ,start_response):
x = self.mapping_urls(environ)
print x
start_response(self.status,self.headers)
if isinstance(x,str):
return iter([x])
else:
return iter(x)
def mapping_urls(self,environ):
path = environ['PATH_INFO']
for pattern,name in self.urls:
m = re.match('^'+pattern+'$',path)
if m:
args = m.groups()
func = globals()[name]
return func(*args)
return self.notfound()
def notfound(self):
self.status = '404 Not Found'
self.headers = [('Content-Type','text/plain')]
return '404 Not Found\n'
@classmethod
def header(cls,name,value):
cls.headers.append((name,value))
def GET_index(*args):
WSGIapp.header('Content-Type','text/plain')
return 'Welcome!\n'
def GET_hello(*args):
WSGIapp.header('Content-Type','text/plain')
return 'Hello %s!\n' % args
urls = [
('/','GET_index'),
('/hello/(.*)','GET_hello')
]
wsgiapp = WSGIapp(urls)
if __name__ == '__main__':
from wsgiref.simple_server import make_server
httpd = make_server('',8000,wsgiapp)
print 'server starting...'
httpd.serve_forever()
上面的代碼是不是很簡介了,只需要定義函數即可。
