编写一个轻量级的Web应用框架
最近阅读了Flask的源码,弄懂了原理之后就想尝试来实现自己的一个Web框架。
因为大部分的实现思路都参照Flask0.1版本,也就是最初版本的思路。所用的基本库是werkzeug。
框架的完整代码都放在了Github上,之后会继续更新:https://github.com/jyz0309/WebFrame
求star,球球了T_T
在完成了ORM模块之后,接下来为框架增加session
的功能,session
的功能原本是想不完全按照flask
的实现,但是发现有点问题,所以还是按照了flask框架的基本思路来实现,也就是底层库的SecureCookie
来实现,并通过栈的方式来查找。
import os
from werkzeug.wrappers import BaseRequest, BaseResponse
from werkzeug.exceptions import HTTPException, MethodNotAllowed, \
NotImplemented, NotFound
from werkzeug.serving import run_simple
from werkzeug.local import LocalStack,LocalProxy
from jinja2 import Environment,FileSystemLoader
from werkzeug.contrib.securecookie import SecureCookie
def render_template(template_name,**context):
'''
:param template_name:模板名字
:param context: 传递给模板的字典参数
:return: template
'''
template_path = os.path.join(os.getcwd(), 'templates')
jinja_env = Environment(loader=FileSystemLoader(template_path), autoescape=True)
return jinja_env.get_template(template_name).render(context)
class Request(BaseRequest):
"""Encapsulates a request."""
class Response(BaseResponse):
"""Encapsulates a response."""
class _RequestContext(object):
"""请求上下文(request context)包含所有请求相关的信息。它会在请求进入时被创建,
然后被推送到_request_ctx_stack,在请求结束时会被相应的移除。它会为提供的
WSGI环境创建URL适配器(adapter)和请求对象。
"""
def __init__(self, app, environ):
self.app = app
self.test = 1
self.request = Request(environ)
self.session = app.open_session(self.request)
def __enter__(self):
# print(self.test)
_request_stk.push(self)
def __exit__(self, exc_type, exc_value, tb):
# 在调试模式(debug mode)而且有异常发生时,不要移除(pop)请求堆栈。
# 这将允许调试器(debugger)在交互式shell中仍然可以获取请求对象。
if tb is None or not self.app.debug:
_request_stk.pop()
class View(object):
"""Baseclass for our views."""
def __init__(self):
self.methods_meta = {
'GET': self.GET,
'POST': self.POST,
'PUT': self.PUT,
'DELETE': self.DELETE,
}
def GET(self):
raise MethodNotAllowed()
POST = DELETE = PUT = GET
def HEAD(self):
return self.GET()
def dispatch_request(self, request, *args, **options):
if request.method in self.methods_meta:
return self.methods_meta[request.method](request, *args, **options)
else:
return '<h1>Unsupported require method</h1>'
@classmethod
def get_func(cls):
def func(*args, **kwargs):
obj = func.view_class()
return obj.dispatch_request(*args, **kwargs)
func.view_class = cls
return func
class App(object):
# 如果设置了密钥,加密组件可以使用它来作为cookies或其他东西的签名。
secret_key = None
debug = True
def __init__(self):
self.url_map = {}
# self.view_functions = {}
def request_context(self,environ):
return _RequestContext(self,environ)
def process_response(self,response):
session = _request_stk.top.session
if session is not None:
self.save_session(session,response)
return response
def wsgi_app(self,environ,start_response):
with self.request_context(environ):
req = Request(environ)
response = self.dispatch_request(req)
if response:#如果可以找到正确的匹配项
response = Response(response, content_type='text/html; charset=UTF-8')
response = self.process_response(response)
else:#找不到,返回404NotFound
response = Response('<h1>404 Not Found<h1>', content_type='text/html; charset=UTF-8', status=404)
return response(environ, start_response)
def open_session(self, request):
"""创建或打开一个新的session,默认的实现是存储所有的session数据到一个签名的cookie中,前提是secret_key属性被设置
:param request: Request实例
"""
key = self.secret_key
if key is not None:
return SecureCookie.load_cookie(request, 'session', secret_key=key)
def save_session(self, session, response):
#print(response)
if session is not None:
session.save_cookie(response)
def __call__(self, environ, start_response):
return self.wsgi_app(environ, start_response)
def dispatch_request(self, req):
try:
url = req.path
view = self.url_map.get(url,None)
if view:
response = view(req)
#print(response)
else:
response = None
except HTTPException as e:
response = e
return response
def add_url_rule(self,urls):
for url in urls:
self.url_map[url] = urls[url].get_func()
# self.url_map[url['url']] = url['func'].get_func()
def run(self, port=8090, ip='127.0.0.1', debug=True):
run_simple(ip, port, self, use_debugger=debug, use_reloader=True)
_request_stk = LocalStack()
current_app = LocalProxy(lambda: _request_stk.top.app)
request = LocalProxy(lambda: _request_stk.top.request)
session = LocalProxy(lambda: _request_stk.top.session)
测试函数
from app import App,View,session
import json
class Index(View):
def GET(self,request,x):
#session['hello'] = 2
return x
def POST(self,request):
print(json.dumps(request.form['color']))
return json.dumps({'1':'hello'})
class Test(View):
def GET(self,request):
# print(session['hello1'])
return 'test'
def POST(self,request):
return json.dumps({'2':'hello'})
urls = {'/test':Test}
app = App()
app.secret_key = 'password'
app.add_url_rule(urls)
app.run()