简单介绍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标准库之itertools库的使用方法
Sep 07 Python
Python实现简单文本字符串处理的方法
Jan 22 Python
Python基于多线程操作数据库相关问题分析
Jul 11 Python
Django migrations 默认目录修改的方法教程
Sep 28 Python
pygame实现俄罗斯方块游戏(对战篇1)
Oct 29 Python
Python中如何将一个类方法变为多个方法
Dec 30 Python
python实现的批量分析xml标签中各个类别个数功能示例
Dec 30 Python
python判断变量是否为int、字符串、列表、元组、字典的方法详解
Feb 13 Python
关于tf.matmul() 和tf.multiply() 的区别说明
Jun 18 Python
浅谈优化Django ORM中的性能问题
Jul 09 Python
python 安装移动复制第三方库操作
Jul 13 Python
django学习之ajax post传参的2种格式实例
May 14 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
ADODB的数据库封包程序库
2006/12/31 PHP
PHP 日期时间函数的高级应用技巧
2009/10/10 PHP
PHP使用http_build_query()构造URL字符串的方法
2016/04/02 PHP
如何在PHP中使用AES加密算法加密数据
2020/06/24 PHP
phpstudy2020搭建站点的实现示例
2020/10/30 PHP
一个不错的应用,用于提交获取文章内容,不推荐用
2007/03/03 Javascript
hover的用法及live的用法介绍(鼠标悬停效果)
2013/03/29 Javascript
使用jQuery解决IE与FireFox下createElement方法的差异
2013/11/14 Javascript
JavaScript实现LI列表数据绑定的方法
2015/08/04 Javascript
jQuery动画效果实现图片无缝连续滚动
2016/01/12 Javascript
利用css+原生js制作简单的钟表
2020/04/07 Javascript
Chrome不支持showModalDialog模态对话框和无法返回returnValue问题的解决方法
2016/10/30 Javascript
Angular.Js的自动化测试详解
2016/12/09 Javascript
vue进行图片的预加载watch用法实例讲解
2018/02/07 Javascript
Layui数据表格之获取表格中所有的数据方法
2018/08/20 Javascript
webuploader分片上传的实现代码(前后端分离)
2018/09/10 Javascript
JS 自执行函数原理及用法
2019/08/05 Javascript
Nodejs文件上传、监听上传进度的代码
2020/03/27 NodeJs
[03:55]2016国际邀请赛中国区预选赛首日TOP10精彩集锦
2016/06/27 DOTA
[40:53]完美世界DOTA2联赛PWL S3 Magma vs DLG 第二场 12.18
2020/12/20 DOTA
python Celery定时任务的示例
2018/03/13 Python
Python JSON格式数据的提取和保存的实现
2019/03/22 Python
详解python常用命令行选项与环境变量
2020/02/20 Python
python3.7+selenium模拟淘宝登录功能的实现
2020/05/26 Python
基于Modernizr 让网站进行优雅降级的分析
2013/04/21 HTML / CSS
澳大利亚第一的设计师礼服租赁网站:GlamCorner
2017/08/13 全球购物
Invicta手表官方商店:百年制表历史的瑞士腕表品牌
2019/09/26 全球购物
Farfetch巴西官网:奢侈品牌时尚购物平台
2020/10/19 全球购物
4s店总经理岗位职责
2013/12/31 职场文书
公交公司毕业生求职信
2014/02/15 职场文书
教师师德承诺书
2014/03/26 职场文书
会计专业毕业生求职信
2014/07/04 职场文书
写给导师的自荐信
2015/03/06 职场文书
出纳岗位职责范本
2015/03/31 职场文书
三八红旗手先进事迹材料(2016推荐版)
2016/02/25 职场文书
励志正能量20句:送给所有为梦想拼搏的人
2019/11/11 职场文书