深入了解wsgi协议及用webob模块处理web请求和响应的方法

和相应内容给web服务器。

深入了解wsgi协议及用webob模块处理web请求和响应的方法

在上述操作中,所采用的开发服务器与程序间交互的通信协议被称作WSGI,这一协议与CGI相似,它也将接收到的请求信息封装成键值对的形式。

仅仅是因为CGI是通过环境变量将信息传递给CGI应用程序的,而WSGI则是直接利用Python的字典对象进行信息传递。

hello_app的第一个参数environ代表一个包含请求信息的字典,第二个参数则是一个函数,该函数负责web应用程序输出响应内容。

前需要先调用它来输出状态码和响应头。

在处理网络请求与反馈的过程中,我们选用webob模块进行操作。为了确保模块的正常运行,必须进行安装。安装过程中,首先需要安装setuptools模块,这是一款包管理工具。它能够自动下载所需的软件包,其功能与Ubuntu系统中的app-get类似。以下是安装后的操作指引:完成安装后,您可在命令行界面键入“easy_install webob”,系统将自动执行下载并完成安装。

简单使用:

>>> # 导入 Request 对象

>>> from webob import Request

>>> environ = {}

利用 Request 对 environ 字典进行封装处理。

>>> req = Request(environ)

通过创建一个名为Request的类来封装environ,我们得以借助Request对象的属性与函数来访问environ。鉴于仅在web环境中方可获取到真实的environ字典,为了便于大家于shell环境下进行测试,webob提供了一种模拟简单web请求的手段:

也可以通过req查找其它有用的信息

同时,我们还可以利用webob模块中的Response实体来对响应内容进行封装。

下面使用webob模块重写之前的hello_app

# -*- coding: utf-8 -*-
导入wsgiref模块中的simple_server子模块。
导入webob模块中的Request和Response类。
我们额外添加了一项功能,该功能能够根据用户在网址末尾提供的参数进行操作。
# 显示相应的内容
def hello_app(request):
 content = []
 # 获取 get 请求的参数
content中添加了字符串'Hello',紧随其后的是从request.GET字典中获取的'name'键对应的值。
 # 输出所有 environ 变量
遍历请求的环境变量,对每个键值对进行操作:key, value := request.environ.items().each()
content中添加了键值对,具体为:%s对应%s。
response = Response(内容以换行符连接后形成的字符串作为正文)
response的headers属性中,将content-type的值设置为text/plain。
 return response
# 对请求和响应进行包装
定义函数wsgi_wrapper,接收两个参数:环境变量environ和启动响应函数start_response。
 request = Request(environ)
 response = hello_app(request)
response 对象同样具备与 wsgi 服务器进行通信的能力,并遵循相应的协议规范。
 # 所以可以帮我们处理与web服务器之间的交互。
 # 这一句比较奇怪,对象使用括号是什么意思。。。。
执行返回操作,向响应函数传递环境变量和启动响应参数。
创建了一个服务器实例,该实例由simple_server模块生成,监听本地主机的8080端口,并使用wsgi_wrapper作为其包装器。
server.serve_forever()

为了提升wsgi_wrapper的适用性,我们可将其构思为一种装饰器模式。

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from webob import Request, Response
# 写成装饰器的 wsgi_wrapper
def wsgi_wrapper(func):
定义一个名为new_func的函数,该函数接收两个参数:环境变量environ和响应启动函数start_response。
  request = Request(environ)
  response = func(request)
执行返回操作,向响应函数传递环境变量和启动响应。
new_func的名称与func的名称相同,即:new_func.__name__ 等于 func.__name__。
new_func的文档字符串被设置为func的文档字符串。
 return new_func
# 应用程序
@wsgi_wrapper
def hello_app(request):
 content = []
 content.append('Hello %s'%request.GET['name'])
 for key, value in request.environ.items():
  content.append('%s : %s' % (key, value))
 response = Response(body='\n'.join(content))
 response.headers['content-type'] = 'text/plain'
 return response
创建了一个服务器实例,该实例由simple_server模块生成,并指定了本地地址为localhost和端口号8080,同时将hello_app作为应用对象传入。
server.serve_forever()

三、模板

果不其然,我们确实需要借助模板,不能总是直接在响应内容中输入一长串的HTML代码。

Python领域常用的模板引擎包括mako、genshi以及jinjia等。

Mako的显著优势在于其模板中能够便捷地嵌入Python代码,并且其执行效能十分卓越。

genshi的特色在于其基于XML的简洁易学的模板语法,这对于那些热衷于xhtml的朋友来说,无疑是一个理想的选择。

同时也可以嵌入Python 代码,实现一些复杂的展现逻辑;

Jinja与Genshi相似,都具备简洁的模板语法,而且它们并不要求遵循XML的规范,因此同样非常适合设计人员直接进行模板的构建。

同时也可以嵌入Python 代码实现一些复杂的展现逻辑。

在此情况下,我们采用Mako工具,通过指定具体地址,执行下载python的setup.py安装命令来完成安装过程。

简单的模块例子:

## -*- coding: utf-8 -*-

 
 简单mako模板
 
 
 
Hello ${name}!
    遍历data字典中的每个键值对,对每个键和对应的值进行处理:for key, value in data.items():
  • ${key} - ${value}
  • % endfor

将文件命名为simple.html,接着向模板对象提供data和name两个参数,之后执行渲染操作,即可输入HTML内容。

# -*- coding: utf-8 -*-
# 导入模板对象
引用自mako模板库中的模板类
# 使用模板文件名构造模板对象
创建模板对象时,指定文件名为'./simple.html',并设定输出编码为'utf-8'。
# 构造一个简单的字典填充模板,并print出来
执行模板渲染操作,其中模板名称设定为'python',并传入包含键值对的数据字典,字典内包含'a'键对应值为1和'b'键对应值为2。

将文件命名为test_template.py,执行后即可输入所需内容。

$ python test_template.py


 
 简单mako模板
 
 
 
Hello python!
  • a - 1
  • b - 2

下面对hello_app程序进行重构:

将 wsgi_wrapper 独立提取至通用模块,命名为 utils.py。

# -*- coding: utf-8 -*-
from webob import Request
def wsgi_wrapper(func):
 def new_func(environ, start_response):
  request = Request(environ)
  response = func(request)
  return response(environ, start_response)
 new_func.__name__ = func.__name__
 new_func.__doc__ = func.__doc__
 return new_func

将“hello_app”完全分离,构建为一个独立的模块,命名为“controller.py”。

# -*- coding: utf-8 -*-
引入模块,使用wsgi_wrapper工具。
from webob import Response
from mako import Template
# 整合了模板功能的 hello_app
@wsgi_wrapper
def hello_app(request):
创建模板对象时,指定文件名为'./simple.html',并设置输出编码为'utf-8'。
content = tmpl执行渲染操作,其中name参数为request.GET字典中获取的'name'键对应的值,data参数则为request.environ环境变量。
 return Response(body=content)

3. 这样 main.py 就变成这样了:

# -*- coding: utf-8 -*-
from wsgiref import simple_server
导入控制器模块中的hello_app应用。
server = simple_server.make_server('localhost', 8080, hello_app)
server.serve_forever()

四、对象关系映射(ORM,即Object Relation Mapping)

这一阶段终于到来,对于web应用而言,与数据库的协作是必不可少的。

此处采用sqlalchemy库,该库属于对象-关系映射(ORM)类别,负责实现Python对象与关系型数据库间的数据映射。与Django的models模块类似,它也实现了这一功能。

其用法颇为相似,同样能够借助Python编写的代码来构建数据库中的表格,并对它们执行各种操作。

SQLAlchemy 能够自动处理 Python 对象的继承关系,支持预加载(eager loading)和延迟加载(lazy loading)功能,并能直接将模型(Model)与自定义的数据库表结构进行映射。

SQL语句的功能十分全面,兼容众多数据库系统。以SQLAlchemy为例,它不仅具备与Hibernate相媲美的强大功能,而且依然保持了Python语言的特色。

的简洁优雅。

使用方法:

# -*- coding: utf-8 -*-
from sqlalchemy import *
导入sqlalchemy.orm模块中的sessionmaker和scoped_session类。
在构建数据库引擎的过程中,我们选择直接运用Python 2.5版本内嵌的数据库引擎,即sqlite。
# 直接在当前目录下建立名为 data.db 的数据库
创建数据库引擎时,指定了使用SQLite数据库,并将数据存储在名为'data.db'的文件中,执行了如下代码:engine = create_engine('sqlite:///data.db')。
关于session的详细内容,请查阅相关资料:http://www.sqlalchemy.org/docs/05/session.html。
创建会话时,我们采用了作用域会话模式,设置了不自动提交和不自动刷新的特性,并将数据库引擎与之绑定。
Base = declarative_base()
class Dictionary(Base):
 # Python 对象对应关系数据库的表名
该表的名称设定为't_dictionary'。
自动定义,参数包括:数据库列名称,列数据类型,以及其他相关选项。
定义一个名为 'key' 的列,其数据类型为字符串,长度不超过255个字符,且该列被设置为该表的主键。
该字段定义为名为'value'的列,数据类型为字符串,最大长度为255个字符。
# 创建数据库
Base.metadata.execute('CREATE ALL TABLES'),engine负责执行该操作。
session = Session()
对于列表中的每个项目,例如'python'、'ruby'以及'java',都执行以下操作:
 # 构造一个对象
字典对象被创建,其键为item,值为item转换为大写的版本。
 # 告诉 sqlalchemy ,将该对象加到数据库
 session.add(dictionary)
在此环节提交会话,数据库操作得以实际执行,向数据库中成功插入三条新记录。
session.commit()
# 查询数据库中Dictionary对象对应的数据
在会话中,对于每个通过session查询得到的字典对象,
输出字典的键,以及对应的值。

执行上述代码两次后会出现错误,其原因是数据库中主键信息出现了重复。

这样就可以整合到之前的controller.py文件中

# -*- coding: utf-8 -*-
from utils import wsgi_wrapper
from webob import Response
from mako.template import Template
# 导入公用的 model 模块
导入Session和Dictionary模块自模型中。
@wsgi_wrapper
def hello_app(request):
 session = Session()
 # 查询到所有 Dictionary 对象
session中查询到的字典对象被赋值给变量dictionaries
接着,利用 Dictionary 对象的键、值属性,将列表成功转化为一个字典。
data等价于一个字典,它通过列表推导式生成,其中每个元素都是一个键值对,键是dictionary的key,值是dictionary的value,这些键值对来源于dictionaries中的每个dictionary。
 tmpl = Template(filename='./simple.html', output_encoding='utf-8')
content = tmpl执行渲染操作,其中使用到的名字参数来源于请求中的GET数据,具体为'name'键对应的值,同时渲染过程中所需的数据由变量data提供。
 return Response(body=content)

五、URL分发控制

针对各类资源,设定各自独特的网址标识,当客户端发起对该网址的请求时,web应用程序便会依据该网址来确定相应的功能模块,并执行相应的操作。

提供一个干净的 URL 有很多好处:

1. 可读性,通过 URL 就可以大概了解其提供什么功能

2. 用户容易记住也方便直接输入

设计精良的URL通常较短且有力,同时更易于搜索引擎的检索和识别。

使用selector模块来处理url映射

请访问指定的下载链接,下载相应的source文件,然后执行python setup.py install命令进行安装。

首先把urls的配置单独放到urls.py中

# -*- coding: utf-8 -*-
from controller import hello_app
配置项中包含了如下映射:('/hello/{name}',),其中定义了当接收到GET请求时,将调用名为hello_app的应用程序。

修改main.py

# -*- coding: utf-8 -*-
from wsgiref import simple_server
from urls import mappings
from selector import Selector
# 构建 url 分发器
app = Selector(mappings)
simple_server模块创建了一个服务器实例,该实例监听在本地主机的8080端口,并绑定到app应用。
server.serve_forever()

在hello_app中,您可以通过访问environ字典中的键'wsgiorg.routing_args'来获取name参数。

尽管在 wsgi_wrapper 中,我们还可以对 hello_app 的功能进行进一步的精简:我们可以直接将解析后的参数,

当作函数参数传过去!修改 utils.py:

from webob import Request
def wsgi_wrapper(func):
 def new_func(environ, start_response):
  request = Request(environ)
environ对象尝试获取名为'wsgiorg.routing_args'的键值,若该键值不存在,则返回一个包含空元组与空字典的元组。即,position_args和keyword_args分别被赋值为environ.get('wsgiorg.routing_args', ((), {}))的结果。
response = 执行函数func,传入参数request,以及剩余的位置参数*position_args,以及关键字参数**keyword_args。
  return response(environ, start_response)
 new_func.__name__ = func.__name__
 new_func.__doc__ = func.__doc__
 return new_func

那 hello_app 就可以改成这样了:

...
@wsgi_wrapper
定义函数hello_app,接收参数request和name,其中name的默认值为空字符串。
 ...
执行模板渲染操作,将变量name和数据data代入模板,生成content变量。
 return Response(body=content)
运行main.py脚本,通过访问网址http://localhost:8080/hello/来使用Python功能。

总结

以上所述功能的实现,与Django框架中的若干核心模块相仿,期待这些内容能对各位的学习带来一定的帮助。

你可能想看:
« 上一篇
下一篇 »

相关推荐

python爬虫教程PDF,Python爬虫书PDF

2026年09月02日

34阅读

外国教程python,python编程教学视频

2026年09月02日

28阅读

python简明教程沈洁元,python详细基础教程

2026年09月02日

22阅读

python库包教程,python 包

2026年09月02日

27阅读

python教程大全集,python教程600集

2026年09月02日

28阅读

python入门教程完整版,python零基础入门全套教程视频

2026年09月02日

23阅读