上一节介绍了什么是WSGI,这一节我们看看Werkzeug

按照官方的说法,Werkzeug(源自德语,工具的意思)是一个WSGI工具库,它开始于一个适用于WSGI的多样化的工具集,后来发展成了现在非常流行的WSGI工具库。Werkzeug可以在程序中单独使用,也作为许多Python Web框架的底层库,例如现在非常流行的Flask Web框架。

Werkzeug的基本功能

正如官方的说法,Werkzeug提供了非常丰富的功能,但是其功能总的可分为两个方面:开发测试方面的功能和其用于Web程序中的工具函数及工具类

开发测试方面

一、Werkzeug提供了一个简易的开发用服务器

二、Werkzeug提供了一些测试工具,如Client类、EnvironBuilder类。

三、Werkzeug提供了Debug的工具,提供了可用于Debug的中间件。当程序出错时,并不会返回500错误,而是显示程序出错的地方以及出错的原因,这就为程序的开发提供了方便。

工具方面

Werkzeug主要提供了如下几种工具

一、请求和相应对象。

提供了RequestResponseRequest可以包装WSGI服务器传入的environ参数,并对其进行进一步的解析,以使我们更容易的使用请求中的参数。Response可以根据传入的参数,来发起一个特定的响应。你可以认为Response是你可以创建的另一个标准的WSGI应用,这个应用可以根据你传入的参数,来帮你做发起响应这件事。

from werkzeug.wrappers import Request, Response

def application(environ, start_response):
request = Request(environ)
response = Response("Hello %s!" % request.args.get('name', 'World!'))
return response(environ, start_response)

二、路由解析。

Werkzeug提供了强大的路由解析功能。比如Flask框架中经常用到的RuleMap类等。

如下面一个程序。

from werkzeug.routing import Map, Rule, NotFound, RequestRedirect

url_map = Map([
Rule('/', endpoint='blog/index'),
Rule('/<int:year>/<int:month>/<int:day>/', endpoint='blog/archive'),
Rule('/about', endpoint='blog/about_me'),
Rule('/feeds/<feed_name>.rss', endpoint='blog/show_feed')
]) def application(environ, start_response):
urls = url_map.bind_to_environ(environ)
try:
endpoint, args = urls.match()
except HTTPException, e:
return e(environ, start_response)
start_response('200 OK', [('Content-Type', 'text/plain')])
return ['Rule points to %r with arguments %r' % (endpoint, args)]

我们创建了一个Map类实例url_map来保存一系列的URL规则。并且给它传递了一个Rule对象的列表。其中,每个Rule对象都包含两个参数:一个字符串和endpoint。字符串代表了URL匹配的规则(也叫路由规则),endpoint(也叫端点)代表了该路由规则对应的视图函数。即当对一个URL匹配成功后,便可获取到它对应的视图函数。不同的规则可以对应相同的endpoint,但是必须有不同的参数用于URL的构建,不能产生歧义,类似于函数的重载。

application函数中,我们使用Mapbind_to_environ方法将url_mapenviron绑定,这会返回给我们一个新的MapAdapter对象,这个对象可用于URL的匹配。随后,我们调用MapAdapter对象中的match()方法,获取当前请求的URL匹配到的endpoint和其参数信息,最后,我们用获取到的endpoint和参数信息发起一个响应。

用于匹配URL的路由规则字符串是由基本的URL加上占位符组成的。

例如Rule('/pages/<path:page>'),尖括号中,冒号后面为变量名,前面为变量的类型。path类型表示只匹配路径。这里的path也可以是int,表示匹配一个整型以及float等。

当不包含尖括号中的变量不写明类型时,如Rule('/pages/<page>'),这里的page可以匹配任何字符串,但是只能就受一个路径段,因此不能含有/

更详细的规则还请参见文档

三、本地上下文

在许多Web程序中,本地上下文是个非常重要的概念。而实现本地上下文需要用到不同线程间数据的隔离。werkzeug.local中定义了LocalLocalStackLocalProxy等类用于实现全局数据的隔离。

在Python中,我们可以使用thread locals来保证多线程状态下数据的隔离,但是这在Web程序中,却并不是很好使。

  • 一是因为有些Web应用是使用协程实现的,无法保证数据的隔离。
  • 二是即使使用的是线程,WSGI也不能保证每次请求使用的线程都是一个全新的线程,可能是一个之前请求的线程,而里面的数据也是原线程剩下的。

所以,Werkzeug给我们提供了Local这个更好用的解决工具。

下面是一个如何使用werkzeug.local例子:

from werkzeug.local import Local, LocalManager

local = Local()
local_manager = LocalManager([local]) def application(environ, start_response):
local.request = request = Request(environ)
... application = local_manager.make_middleware(application)

可以看到,我们把一个Request对象赋值给了全局对象local.request,这样,我们就可以在全局范围内使用local.request,而且获取到的仅仅是当前请求的数据。因为Local对象不会在请求结束后自动清除本地上下文,所以这里我们需要使用LocalManager来管理。我们需要将管理的Local对象以列表的方式传给LocalManager,并在最后使用LocalManagermake_middleware方法为WSGI程序添加中间件,来使请求结束后自动清除本次请求的数据。

那么Local是如何实现的呢?其实很简单,在Local中,重写了__getattr____setattr__方法,使得在获取数据和存储数据之前,先获取到线程id(或协程id),以线程id(或协程id)为键,数据为值,存储在一个字典中。这样我们在操作数据的时候,操作的只会是当前线程(或协程)的数据,从而实现了数据隔离。感兴趣的同学可以查看一下文末Local的源码。

LocalStackLocal进行了封装,使其可以以栈的方式使用。如下:

>>> ls = LocalStack()
>>> ls.push(42)
>>> ls.top
42
>>> ls.push(23)
>>> ls.top
23
>>> ls.pop()
23
>>> ls.top
42

LocalProxy类用于实现werkzeug本地代理,将所有的操作转发给代理对象。如果你熟悉C++的话,你会发现这和C++的引用很像,但比引用更强大。使用方法如下:

from werkzeug.local import Local
l = Local() # 以下是两个代理
request = l('request') # Local中实现了__call__方法,用于返回一个代理,具体可以查看文末Local的源码
user = l('user') from werkzeug.local import LocalStack
_response_local = LocalStack() # 这也是个代理
response = _response_local() # 同理,LocalStack返回的也是代理

除了以上创建代理的方式外,还可以手动创建一个代理

from werkzeug.local import Local, LocalProxy
local = Local()
request = LocalProxy(local, 'request')

如果你想拥有一个根据指定函数来返回不同的对象代理,也是支持的。

session = LocalProxy(lambda: get_current_request().session)

但我们为什么要使用代理呢。这里简单说一下,我们知道,一个变量被赋值后如果不重新赋值,它的值是不会改变的,那么这在程序的某些地方就会变得很不方便。但是如果使用代理的话,那么我们在使用这个变量的时候就能动态的获取到它所代理的对象的最新的值。

四、其他

除了上面三个方面外,Werkzeug还提供了很多工具,例如WSGI中间件、HTTP异常类、数据结构等。这里就不在一一详述,感兴趣的同学可以参考文档


Local对象部分源码:

try:
from greenlet import getcurrent as get_ident
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident class Local(object):
__slots__ = ('__storage__', '__ident_func__') def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident) def __iter__(self):
return iter(self.__storage__.items()) def __call__(self, proxy):
"""Create a proxy for a name."""
return LocalProxy(self, proxy) def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None) # 清除数据 def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name) def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value} def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)

参考:

本篇参考Werkzeug文档写成,如有错误或与文档不符的地方,还请以文档为准,也欢迎您反馈给我。

什么是Werkzeug的更多相关文章

  1. Werkzeug工具包学习-官方例子Shortly分析

    为了学习werkzeug的wsgi框架工具,今天真对官网的例子进行调试运行.涉及到了werkzeug工具包,jinja2前端模版,以及redis内存库,之后可以灵活定制自己主页.再次,作以记录. 首先 ...

  2. Werkzeug教程

    http://chaoxz2005.blog.163.com/blog/static/15036542012863405266/ http://www.dajo.com.cn/a/boke/pytho ...

  3. Flask学习记录之使用Werkzeug散列密码

    数据库中直接存放明文密码是很危险的,Werkzeug库中的security能够方便的实现散列密码的计算 security库中 generate_password_hash(password,metho ...

  4. Werkzeug源码阅读笔记(四)

    今天主要讲一下werkzeug中的routing模块.这个模块是werkzeug中的重点模块,Flask中的路由相关的操作使用的都是这个模块 routing模块的用法 在讲解模块的源码之前,先讲讲这个 ...

  5. Werkzeug源码阅读笔记(三)

    这次主要讲下werkzeug中的Local. 源码在werkzeug/local.py Thread Local 在Python中,状态是保存在对象中.Thread Local是一种特殊的对象,它是对 ...

  6. werkzeug源码阅读笔记(二) 下

    wsgi.py----第二部分 pop_path_info()函数 先测试一下这个函数的作用: >>> from werkzeug.wsgi import pop_path_info ...

  7. werkzeug中服务器处理请求的实现

    当成功建立好服务器后,接下来就是等待请求并处理请求通过路由分配给相应的视图函数了,以下是函数调用过程 -> self._handle_request_noblock() /usr/lib/pyt ...

  8. werkzeug中reloader的实现

    在用flask开发时,如果把use_reloader设为True(debug设为True也能实现),那当你修改了app代码或调用环境发生改变时,服务器会自动重启,如下 * Detected chang ...

  9. Flask之WSGI:Werkzeug

    WSGI 一个Web应用的本质就是: 浏览器发送一个HTTP请求: 服务器收到请求,生成一个HTML文档: 服务器把HTML文档作为HTTP响应的Body发送给浏览器: 浏览器收到HTTP响应,从HT ...

  10. 关于flask自带web应用服务器Werkzeug 使用requests请求时出现的错误。

    先说明一下当时的情况,下午遇到一个需求需要先从jd那边拿到图片然后上传到本地的cdn服务器来获取对应的ident 和地址. 于是就需要首先拿到京东的图片url,然后按照图片url去请求图片到内存然后再 ...

随机推荐

  1. <<Modern CMake>> 翻译 2.4 项目目录结构

    <<Modern CMake>> 翻译 2.4 项目目录结构 本节内容有点跑题.但我认为这是一个很好的方法. 我将告诉你如何规划项目的目录. 这是基于惯例,但将帮助您: 轻松阅 ...

  2. oracle 正确删除归档日志,并清除 V$ARCHIVED_LOG 数据

    1. 连接 RMAN 管理 rman target / 2. 查看归档日志列表 RMAN> crosscheck archivelog all; 3. 删除所有归档日志 RMAN> DEL ...

  3. Unity通过NTP获取网络时间

    最初通过qq时间服务器获得时间,经常出现有网络也获取失败的情况. 后面寻找解决办法,查找资料终于发现通过ntp时间服务器获取网络时间的方法.   首先游戏开始获得初始化网络时间,通常只获取一次,其他时 ...

  4. 算法实战-OJ之旅

    算法虽然不是特别简单,但没有你想象中的那么难. Sort Array By Parity easy AC-17ms. 按照<算法导论>排序一章的一些概念,第二种可以称为是原址的(in-pl ...

  5. node获取本机动态IP,并对应修改相关JavaScript文件的IP地址

    目录 由于本机是自动获取分配的动态IP,所以每次重启后需要重新更改与IP相关文件 参考 时间:2018-08-02,更新时间:2018-11-06 注意:在win10环境运行无问题 由于本机是自动获取 ...

  6. C语言编程入门之--第五章C语言基本运算和表达式-part2

    5.1.4 再来一个C库函数getchar吸收回车键 回车键也是一个字符,在使用scanf的时候,输入完毕要按下回车键,这时候回车键也会被输入到stdin流中,会搞乱我们的程序. 注意:stdin是输 ...

  7. Git下载加速教程

    方法一 大家普遍采取的是更改本地的host文件,然后cmd命令刷新 1.访问这里,依次获取下面三个url的ping的ip github.com github.global.ssl.fastly.net ...

  8. (二)c#Winform自定义控件-按钮

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  9. AutoResetEvent控制线程用法

    本文主要来自一道面试题,由于之前对AutoResetEvent的用户很模糊(即使已经使用过了).面试题题目很简洁:两个线程交替打印0~100的奇偶数.你可以先动手试试,我主要是尝试在一个方法里面完成这 ...

  10. 基于STM32F429和Cube的ov2640程序

    1.ov2640和DCMI介绍 OV2640 是 OV(OmniVision)公司生产的一颗 1/4 寸的 CMOS UXGA(1632*1232)图 像传感器.该传感器体积小.工作电压低,提供单片 ...