利用python的werkzeug写的一个简单的web框架。实现的功能有路由、各种HTTP方法、获取客户端传递的参数、代码实时更新、模板引擎等。默认运行在127.0.0.1:5000上,操作模式也尽量向Flask靠拢。
import os from werkzeug.wrappers import Response, Request from werkzeug.serving import run_simple from werkzeug.routing import Map, Rule from werkzeug.exceptions import HTTPException, MethodNotAllowed from jinja2 import Environment, FileSystemLoader """ 定义一个Route类来管理url_map以及请求方式. 并加入__call__函数,使得可以直接对对象使用诸如Route()的操作. __call__函数中再使用装饰器,这样操作起来就和Flask一样了 endpoint使用了对应的函数名. 从参数中获取并储存HTTP请求方法 """ class Route: def __init__(self): self.url_map = Map([]) self.endpoint_dict = {} self.methods_list = [] def __call__(self, rules,**kwargs): def wrapper(func): endpoint = func.__name__ self.url_map.add(Rule(rules, endpoint=endpoint)) self.endpoint_dict[endpoint] = func for key, value in kwargs.items(): if key == 'methods': self.methods_list = value return func return wrapper """ 定义了一个Application类 """ class Application(): def __init__(self): self.route = Route()#调用Route类 def __call__(self, environ, start_response): return self.application(environ, start_response) def run(self,host='127.0.0.1',port=5000,use_reloader=False):#用run_simple定义一个运行方法,可以自行选择host和port以及是否进行实时更新代码 run_simple(host, port, self, use_reloader=use_reloader) return def url_for(self,a):#返回对应endpoint的url return self.urls.build(a) @Request.application def application(self, request): self.urls = self.bind_to_environ(request.environ) if request.method not in self.route.methods_list:#如果请求方法不正确则返回MethodNotAllowed页面 raise MethodNotAllowed try: endpoint, args = self.urls.match()#匹配请求参数 except HTTPException as e:#如果找不到url则返回404 return e return Response(self.route.endpoint_dict[endpoint](request), mimetype='text/html')#响应请求,类型为'text/html' def bind_to_environ(self, env):#用以绑定环境 return self.route.url_map.bind_to_environ(env) def getdata(self, arg, *instead):#获取数据,如果没有对应数据则返回instead的值,类似flask的request.get('ex1','ex2') values = self.urls.match()[1] try: return values[arg] except: if instead[0] or instead[0] == '': values[arg] = instead[0] return values[arg] """ 依然是render_template 将templates文件夹绑定了jinja2的环境 通过获取templates里html文件的文本并通过Application里方法响应到客户端 """ def render_template(template_name, **context): template_path = os.path.join(os.getcwd(), 'templates') jinja_env = Environment(loader=FileSystemLoader(template_path), autoescape=True) t = jinja_env.get_template(template_name) return t.render(context)
测试代码
from web import Application,render_template app = Application() @app.route('/',methods=['GET','POST']) def index(request):#这里的func需要带一个request参数 id = app.getdata('id','abaa')#因为方法都封装在Application里了,所以使用的时候都得带上app.的前缀 pid = app.getdata('id','') print('id =',id) print('pid =',pid) return render_template('index.html',aa=id,cc='ddd',list=['1','3','2','4']) @app.route('/id/<int:id>') def id(request): id = app.getdata('id') return ("id is: {id}").format(id=id) @app.route('/current_url') def current_url(request): url = app.url_for("test") return "current url is {url}".format(url=url) @app.route('/testurl') def test(request): return render_template('index.html') app.run(debug=True)
测试模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> hhhhh<br> <h2>jinja2测试</h2> {{aa}}<h1>{{cc}}</h1> {% if cc %} {{aa}}<h3>{{list}}</h3> {%endif%} {%for i in list %} {{i}}<br> {%endfor%} </body> </html>