TECH_WEB互联网科技iOS Developer

用Python写一个简单的Web框架【2】

2017-06-19  本文已影响183人  adc9c8f3920d

五、重构

上面的代码虽然奏效,但是在编码风格和灵活性方面有很多问题,下面逐步对其进行重构。

1、正则匹配URL

消除URL硬编码,增加URL调度的灵活性:

Python

51#!/usr/bin/env python

# -*- coding: utf-8 -*-

"""application.py"""

importre##########修改点

classmy_app:

urls=(

("/","index"),

("/hello/(.*)","hello"),

)##########修改点

def__init__(self,environ,start_response):

self.environ=environ

self.start=start_response

def__iter__(self):##########修改点

path=self.environ['PATH_INFO']

method=self.environ['REQUEST_METHOD']

forpattern,nameinself.urls:

m=re.match('^'+pattern+'$',path)

ifm:

# pass the matched groups as arguments to the function

args=m.groups()

funcname=method.upper()+'_'+name

ifhasattr(self,funcname):

func=getattr(self,funcname)

returnfunc(*args)

returnself.notfound()

defGET_index(self):

status='200 OK'

response_headers=[('Content-type','text/plain')]

self.start(status,response_headers)

yield"Welcome!\n"

defGET_hello(self,name):##########修改点

status='200 OK'

response_headers=[('Content-type','text/plain')]

self.start(status,response_headers)

yield"Hello %s!\n"%name

defnotfound(self):

status='404 Not Found'

response_headers=[('Content-type','text/plain')]

self.start(status,response_headers)

yield"Not Found\n"

2、DRY

消除GET_*方法中的重复代码,并且允许它们返回字符串:

Python

61#!/usr/bin/env python

# -*- coding: utf-8 -*-

"""application.py"""

importre

classmy_app:

urls=(

("/","index"),

("/hello/(.*)","hello"),

)

def__init__(self,environ,start_response):##########修改点

self.environ=environ

self.start=start_response

self.status='200 OK'

self._headers=[]

def__iter__(self):##########修改点

result=self.delegate()

self.start(self.status,self._headers)

# 将返回值result(字符串 或者 字符串列表)转换为迭代对象

ifisinstance(result,basestring):

returniter([result])

else:

returniter(result)

defdelegate(self):##########修改点

path=self.environ['PATH_INFO']

method=self.environ['REQUEST_METHOD']

forpattern,nameinself.urls:

m=re.match('^'+pattern+'$',path)

ifm:

# pass the matched groups as arguments to the function

args=m.groups()

funcname=method.upper()+'_'+name

ifhasattr(self,funcname):

func=getattr(self,funcname)

returnfunc(*args)

returnself.notfound()

defheader(self,name,value):##########修改点

self._headers.append((name,value))

defGET_index(self):##########修改点

self.header('Content-type','text/plain')

return"Welcome!\n"

defGET_hello(self,name):##########修改点

self.header('Content-type','text/plain')

return"Hello %s!\n"%name

defnotfound(self):##########修改点

self.status='404 Not Found'

self.header('Content-type','text/plain')

return"Not Found\n"

3、抽象出框架

为了将类my_app抽象成一个独立的框架,需要作出以下修改:

剥离出其中的具体处理细节:urls配置 和 GET_*方法(改成在多个类中实现相应的GET方法)

把方法header实现为类方法(classmethod),以方便外部作为功能函数调用

改用 具有__call__方法的实例来实现application

修改后的application.py(最终版本):

Python

54#!/usr/bin/env python

# -*- coding: utf-8 -*-

"""application.py"""

importre

classmy_app:

"""my simple web framework"""

headers=[]

def__init__(self,urls=(),fvars={}):

self._urls=urls

self._fvars=fvars

def__call__(self,environ,start_response):

self._status='200 OK'# 默认状态OK

delself.headers[:]# 清空上一次的headers

result=self._delegate(environ)

start_response(self._status,self.headers)

# 将返回值result(字符串 或者 字符串列表)转换为迭代对象

ifisinstance(result,basestring):

returniter([result])

else:

returniter(result)

def_delegate(self,environ):

path=environ['PATH_INFO']

method=environ['REQUEST_METHOD']

forpattern,nameinself._urls:

m=re.match('^'+pattern+'$',path)

ifm:

# pass the matched groups as arguments to the function

args=m.groups()

funcname=method.upper()# 方法名大写(如GET、POST)

klass=self._fvars.get(name)# 根据字符串名称查找类对象

ifhasattr(klass,funcname):

func=getattr(klass,funcname)

returnfunc(klass(),*args)

returnself._notfound()

def_notfound(self):

self._status='404 Not Found'

self.header('Content-type','text/plain')

return"Not Found\n"

@classmethod

defheader(cls,name,value):

cls.headers.append((name,value))

对应修改后的code.py(最终版本):

Python

33#!/usr/bin/env python

# -*- coding: utf-8 -*-

"""code.py"""

fromapplicationimportmy_app

urls=(

("/","index"),

("/hello/(.*)","hello"),

)

wsgiapp=my_app(urls,globals())

classindex:

defGET(self):

my_app.header('Content-type','text/plain')

return"Welcome!\n"

classhello:

defGET(self,name):

my_app.header('Content-type','text/plain')

return"Hello %s!\n"%name

if__name__=='__main__':

fromwsgiref.simple_serverimportmake_server

httpd=make_server('',8086,wsgiapp)

sa=httpd.socket.getsockname()

print'http://{0}:{1}/'.format(*sa)

# Respond to requests until process is killed

httpd.serve_forever()

当然,您还可以在code.py中配置更多的URL映射,并实现相应的类来对请求作出响应。我有建立一个python学习交流群,在群里我们相互帮助,相互关心,相互分享内容,这样出问题帮助你的人就比较多,群号是301,还有056,最后是051,这样就可以找到大神聚合的群,如果你只愿意别人帮助你,不愿意分享或者帮助别人,那就请不要加了,你把你会的告诉别人这是一种分享。如果你看了觉得还可以的麻烦给我点个赞谢谢

上一篇 下一篇

猜你喜欢

热点阅读