简单介绍Python下自己编写web框架的一些要点


Posted in Python onApril 29, 2015

在正式开始Web开发前,我们需要编写一个Web框架。

为什么不选择一个现成的Web框架而是自己从头开发呢?我们来考察一下现有的流行的Web框架:

  • Django:一站式开发框架,但不利于定制化;
  • web.py:使用类而不是更简单的函数来处理URL,并且URL映射是单独配置的;
  • Flask:使用@decorator的URL路由不错,但框架对应用程序的代码入侵太强;
  • bottle:缺少根据URL模式进行拦截的功能,不利于做权限检查。

所以,我们综合几种框架的优点,设计一个简单、灵活、入侵性极小的Web框架。
设计Web框架

一个简单的URL框架应该允许以@decorator方式直接把URL映射到函数上:

# 首页:
@get('/')
def index():
  return '<h1>Index page</h1>'

# 带参数的URL:
@get('/user/:id')
def show_user(id):
  user = User.get(id)
  return 'hello, %s' % user.name

有没有@decorator不改变函数行为,也就是说,Web框架的API入侵性很小,你可以直接测试函数show_user(id)而不需要启动Web服务器。

函数可以返回str、unicode以及iterator,这些数据可以直接作为字符串返回给浏览器。

其次,Web框架要支持URL拦截器,这样,我们就可以根据URL做权限检查:

@interceptor('/manage/')
def check_manage_url(next):
  if current_user.isAdmin():
    return next()
  else:
    raise seeother('/signin')

拦截器接受一个next函数,这样,一个拦截器可以决定调用next()继续处理请求还是直接返回。

为了支持MVC,Web框架需要支持模板,但是我们不限定使用哪一种模板,可以选择jinja2,也可以选择mako、Cheetah等等。

要统一模板的接口,函数可以返回dict并配合@view来渲染模板:

@view('index.html')
@get('/')
def index():
  return dict(blogs=get_recent_blogs(), user=get_current_user())

如果需要从form表单或者URL的querystring获取用户输入的数据,就需要访问request对象,如果要设置特定的Content-Type、设置Cookie等,就需要访问response对象。request和response对象应该从一个唯一的ThreadLocal中获取:

@get('/test')
def test():
  input_data = ctx.request.input()
  ctx.response.content_type = 'text/plain'
  ctx.response.set_cookie('name', 'value', expires=3600)
  return 'result'

最后,如果需要重定向、或者返回一个HTTP错误码,最好的方法是直接抛出异常,例如,重定向到登陆页:

raise seeother('/signin')

返回404错误:

raise notfound()

基于以上接口,我们就可以实现Web框架了。
实现Web框架

最基本的几个对象如下:

# transwarp/web.py

# 全局ThreadLocal对象:
ctx = threading.local()

# HTTP错误类:
class HttpError(Exception):
  pass

# request对象:
class Request(object):
  # 根据key返回value:
  def get(self, key, default=None):
    pass

  # 返回key-value的dict:
  def input(self):
    pass

  # 返回URL的path:
  @property
  def path_info(self):
    pass

  # 返回HTTP Headers:
  @property
  def headers(self):
    pass

  # 根据key返回Cookie value:
  def cookie(self, name, default=None):
    pass

# response对象:
class Response(object):
  # 设置header:
  def set_header(self, key, value):
    pass

  # 设置Cookie:
  def set_cookie(self, name, value, max_age=None, expires=None, path='/'):
    pass

  # 设置status:
  @property
  def status(self):
    pass
  @status.setter
  def status(self, value):
    pass

# 定义GET:
def get(path):
  pass

# 定义POST:
def post(path):
  pass

# 定义模板:
def view(path):
  pass

# 定义拦截器:
def interceptor(pattern):
  pass

# 定义模板引擎:
class TemplateEngine(object):
  def __call__(self, path, model):
    pass

# 缺省使用jinja2:
class Jinja2TemplateEngine(TemplateEngine):
  def __init__(self, templ_dir, **kw):
    from jinja2 import Environment, FileSystemLoader
    self._env = Environment(loader=FileSystemLoader(templ_dir), **kw)

  def __call__(self, path, model):
    return self._env.get_template(path).render(**model).encode('utf-8')

把上面的定义填充完毕,我们就只剩下一件事情:定义全局WSGIApplication的类,实现WSGI接口,然后,通过配置启动,就完成了整个Web框架的工作。

设计WSGIApplication要充分考虑开发模式(Development Mode)和产品模式(Production Mode)的区分。在产品模式下,WSGIApplication需要直接提供WSGI接口给服务器,让服务器调用该接口,而在开发模式下,我们更希望能通过app.run()直接启动服务器进行开发调试:

wsgi = WSGIApplication()
if __name__ == '__main__':
  wsgi.run()
else:
  application = wsgi.get_wsgi_application()

因此,WSGIApplication定义如下:

class WSGIApplication(object):
  def __init__(self, document_root=None, **kw):
    pass

  # 添加一个URL定义:
  def add_url(self, func):
    pass

  # 添加一个Interceptor定义:
  def add_interceptor(self, func):
    pass

  # 设置TemplateEngine:
  @property
  def template_engine(self):
    pass

  @template_engine.setter
  def template_engine(self, engine):
    pass

  # 返回WSGI处理函数:
  def get_wsgi_application(self):
    def wsgi(env, start_response):
      pass
    return wsgi

  # 开发模式下直接启动服务器:
  def run(self, port=9000, host='127.0.0.1'):
    from wsgiref.simple_server import make_server
    server = make_server(host, port, self.get_wsgi_application())
    server.serve_forever()
Try

把WSGIApplication类填充完毕,我们就得到了一个完整的Web框架。

Python 相关文章推荐
Python实现向QQ群成员自动发邮件的方法
Nov 19 Python
Python中实现参数类型检查的简单方法
Apr 21 Python
python使用append合并两个数组的方法
Apr 28 Python
Python中threading模块join函数用法实例分析
Jun 04 Python
python 的列表遍历删除实现代码
Apr 12 Python
Python判断文件或文件夹是否存在的三种方法
Jul 27 Python
matplotlib savefig 保存图片大小的实例
May 24 Python
django.db.utils.ProgrammingError: (1146, u“Table‘’ doesn’t exist”)问题的解决
Jul 13 Python
Python实现企业微信机器人每天定时发消息实例
Feb 25 Python
有趣的Python图片制作之如何用QQ好友头像拼接出里昂
Apr 22 Python
基于Python的图像阈值化分割(迭代法)
Nov 20 Python
python基于tkinter制作下班倒计时工具
Apr 28 Python
编写Python的web框架中的Model的教程
Apr 29 #Python
python获取本地计算机名字的方法
Apr 29 #Python
Python中编写ORM框架的入门指引
Apr 29 #Python
python获取本机mac地址和ip地址的方法
Apr 29 #Python
在Python中编写数据库模块的教程
Apr 29 #Python
Python的gevent框架的入门教程
Apr 29 #Python
在Python中使用HTML模版的教程
Apr 29 #Python
You might like
PHP封装的数据库保存session功能类
2016/07/11 PHP
yii2.0框架场景的简单使用示例
2020/01/25 PHP
Easy.Ajax 部分源代码 支持文件上传功能, 兼容所有主流浏览器
2011/02/24 Javascript
用js设置下拉框为只读的小技巧
2014/04/10 Javascript
js重写alert控件(适合学习js的新手朋友)
2014/08/24 Javascript
angularJS 中input示例分享
2015/02/09 Javascript
使用纯javascript实现经典扫雷游戏
2015/04/23 Javascript
JS模拟bootstrap下拉菜单效果实例
2016/06/17 Javascript
使用HTML5+Boostrap打造简单的音乐播放器
2016/08/05 Javascript
基于JavaScript实现拖动滑块效果
2017/02/16 Javascript
easy ui datagrid 从编辑框中获取值的方法
2017/02/22 Javascript
面试常见的js算法题
2017/03/23 Javascript
React-router v4 路由配置方法小结
2017/08/08 Javascript
微信小程序动态添加和删除组件的现实
2020/02/28 Javascript
微信小程序实现滚动Tab选项卡
2020/11/16 Javascript
12步教你理解Python装饰器
2016/02/25 Python
Python输出各行命令详解
2018/02/01 Python
pandas创建新Dataframe并添加多行的实例
2018/04/08 Python
python微信撤回监测代码
2019/04/29 Python
python做反被爬保护的方法
2019/07/01 Python
python对常见数据类型的遍历解析
2019/08/27 Python
浅析python中while循环和for循环
2019/11/19 Python
python 利用百度API识别图片文字(多线程版)
2020/12/14 Python
Python3爬虫RedisDump的安装步骤
2021/02/20 Python
在HTML5 Canvas中放入图片和保存为图片的方法
2014/05/03 HTML / CSS
不同浏览器创建XMLHttpRequest方法有什么不同
2014/11/17 面试题
营销主管自我评价怎么写
2013/09/19 职场文书
写给女朋友的道歉信
2014/01/08 职场文书
《雕塑之美》教学反思
2014/04/24 职场文书
秋天的图画教学反思
2014/05/01 职场文书
代收款委托书范本
2014/10/01 职场文书
党员教师群众路线个人整改措施
2014/10/28 职场文书
难以忽视的真相观后感
2015/06/05 职场文书
干货:企业内部人才推荐奖励方案!
2019/07/09 职场文书
Vue实现下拉加载更多
2021/05/09 Vue.js
Python matplotlib 利用随机函数生成变化图形
2022/04/26 Python