上一节介绍了什么是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. 深入理解JVM-java字节码文件结构剖析(练习解读字节码)

    public class MyTest2 { String str = "Welcome"; private int x = 5; public static Integer in ...

  2. 数据结构-二叉搜索树和二叉树排序算法(python实现)

    今天我们要介绍的是一种特殊的二叉树--二叉搜索树,同时我们也会讲到一种排序算法--二叉树排序算法.这两者之间有什么联系呢,我们一起来看一下吧. 开始之前呢,我们先来介绍一下如何创建一颗二叉搜索树. 假 ...

  3. ext container的使用的场景

    container 是 panel 简化,他称之为容器,而panel则是面板. 如果不需要类似Ext.panel.Panel,Ext.window.Window和Ext.tab.Panel 等功能,则 ...

  4. Java——擦除

    直接代码分析一波: import java.util.*; public class Ex12 { public static void main(String[] args) { Class c1 ...

  5. 全世界仅有的唯一最高LINUX版本的白菜路由,支持NAND记

    在上上篇 真千兆路由的极限之OPENWRT MAKE, 某品牌白菜价QCA9558/QCA9880/QCA8337N纯种组合OS搭建时记 里,有没有还记否之模式退一步,海阔天空 回到了远古时代的ar7 ...

  6. Git简易使用教程

    1.Git 安装 2.设置git登录信息 3.git操作命令 4.提交代码的过程中几个命令的顺序 5.git 学习资料. 1.Git 安装 Git 下载地址:https://git-scm.com/d ...

  7. (二十)c#Winform自定义控件-有后退的窗体

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

  8. EXP查询合集提权后渗透必备

    0x00 整理的一些后渗透提权需要用到的一些漏洞,后渗透提权的时候可以看一下目标机那些补丁没打,再进行下一步渗透提权. 0x01 CVE-2019-0803 [An elevation of priv ...

  9. 3.php基础(控制语句,函数,数组遍历)

    if条件判断语句 结构一:只判断true,不管false 结构二:既判断true,也判断false(二选一) 结构三:多条件判断 switch多分支结构 Switch语法结构说明: l Switch的 ...

  10. 性能测试学习第三天-----loadrunner接口测试&中文乱码处理

    loadrunner 接口测试:   get.post(3种参数格式).cookie及token处理.加密接口.webservice.socket.文件上传接口.文件下载接口     &  中 ...