最近在实验室也没什么事情做,闲着无聊。于是想着造轮子,最后决定写一个简单的web框架玩玩。
STEP.1
我们首先得选择基于什么协议来写这种框架。我们可以选择CGI协议,或者是WSGI接口。如果使用CGI,实际上我们只是按着CGI的规范写了一个python程序,然后每次服务器收到请求,就fork一个程序来执行它,然后返回一个http文档,性能比较低下。对于WSGI,而是一个存在于服务器和应用间的接口,在WSGI之前,web应用都是依赖于服务器的,现在流行的python框架都支持WSGI接口。
STEP.2 PEP-333
这一段是PEP-333 所提供的样例代码。
def simple_app(environ, start_response): """Simplest possible application object""" status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return ['Hello world!\n']
这里的application被传入了两个值。
environ
start_response。
environ是一个字典,保存了http请求的信息。
start_response是一个函数,发送http响应。她有两个参数status 和 start_headers。status必须是由状态编号和具体信息组成的字符串,必须符合RFC 2616。
start_headers是一个(header_name,header_value) 元组的列表元组列表。其中的hearder_name必须是合法的http header字段名。在RFC 2616, Section 4.2中有详细定义。
当然官方还给出了类的实现。
def __init__(self, environ, start_response): self.environ = environ self.start = start_response def __iter__(self): status = '200 OK' response_headers = [('Content-type','text/plain')] self.start(status, response_headers) yield "Hello world!\n"
了解了如上信息后,基本上可以开始了。我们就到官方给的代码上进行修改吧。
STEP.3 将路径链接到函数
首先我们得把用户请求的路径,链接到函数。我们可以实现一个getPage方法,专门做这件事。我们所拥有的信息,只有environ['PATH_INFO']。
urls = [('^/index/$','func_index'), ('^/comment/$','func_comment'), ('^/environ/$','get_environ'), ('^/post/$','post_test')]#urls是提供给app开发者来做链接的。 def getPage(self): path = self.environ['PATH_INFO'] for pattern in self.urls: m = re.match(pattern[0],path)#将urls元素的第0项和path进行比对,如果匹配上了,返回函数名 if m: function = getattr(self,pattern[1])#getattr方法来得到函数 return function() return '404 not found'#没匹配上任何东西
写到这里之后,每次添加页面,就只需要在urls列表中添加一个元祖就行了。
STEP.4 获取模版
既然是写web app,模版肯定是得有的。这里我提供了一种getTemplate方法来做这件事。不过我只提供了变量的替换。
def getTemplate(self,tem_name,rep=0): #这个函数返回内容,tem_name是文件名字 #参数rep是一个字典,默认为0 f = open('template/'+tem_name) html = f.read() if(rep!=0): for to_replace in rep: strinfo = re.compile('\{\%\s*'+str(to_replace)+'\s*\%\}') html = strinfo.sub(rep[to_replace],html) return html
STEP.5 POST数据的处理
要想获取POST数据,我们得通过environ['wsgi.input']来处理。而他实际上就是系统的标准输入。
environ['wsgi.input'] = sys.stdin
知道这点后就很好写了。
def getPost(self): if(self.environ['REQUEST_METHOD'] == 'POST'): try: request_body_size = int(self.environ.get('CONTENT_LENGTH', 0))#读出content_length的值 except: request_body_size = 0 request_body = self.environ['wsgi.input'].read(request_body_size) #请求的body post_data = parse_qs(request_body)#parse_qs是cgi提供的方法,帮助我们处理请求 return post_data
数据库的链接
import MySQLdbclass Model(object): def __init__(self): self.host = 'localhost' self.port = 3306 self.user = 'admin' self.passwd = 'admin' self.db = 'xieyi' def build_connect(self): self.conn = MySQLdb.connect( host = self.host, port = self.port, user = self.user, passwd = self.passwd, db = self.db ) def exec_ins(self,ins): cur = self.conn.cursor() num = cur.execute(ins) info = {} if(num>0): info = cur.fetchmany(num) cur.close() self.conn.commit() return info def close(self): self.conn.close()
STEP.6 清理工作
很多配置如果放到代码中,会增加阅读负担。于是把urls,model抽取出来。
使得配置更加方便。
STEP.7 结束
这只是一个web框架简易的实现,仅供娱乐。如有疏漏,还望指出。
代码po在https://github.com/bloodycoder/skeleton
作者:查尔德77
链接:https://www.jianshu.com/p/d44ffb679bed