我们首先来谈谈web框架. web框架的本质其实就是socket
服务端再加上业务逻辑处理, 比如像是Tornado
这样的框架. 有一些框架则只包含业务逻辑处理, 例如Django
, bottle
, flask
这些框架, 它们的使用需要依赖包含socket
的第三方模块(即 wsgiref
)来运行
在python中常见的web框架构建模式有以下两种:
- *
MVC
框架: * - 数据库相关操作的
Models
- 视图文件的
Views
- 业务逻辑的
Controllers
MTV
框架:- 数据库相关操作的
Models
- 模板文件
Templates
- 业务逻辑的
Views
以上两种只是命名不同, 所遵循的的思想也只是大同小异
在使用Tornado
框架前, 我们先使用wsgiref
再加上自己写的业务逻辑自定义一个web框架
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from wsgiref.simple_server import make_server
def index():
return "This is index "
def news():
return "welcome to news "
URLS = {
'/index': index,
'/news': news,
}
def RunServer(rq, rp):
rp('200 OK', [('Content-Type', 'text/html')])
url = rq['PATH_INFO']
if url in URLS.keys():
ret = URLS[url]()
else:
ret = '404'
return ret
if __name__ == '__main__':
http = make_server('', 8000, RunServer)
http.serve_forever()
-
wsgiref
在py2中运行正常, 在py3中会报错 -
http = make_server('', 8000, RunServer)
这里创建socket
服务端, 并传入业务逻辑功能函数RunServer(rq, rp)
-
http.serve_forever()
启动服务端, 阻塞进程等待客户端访问, 一旦有访问则执行RunServer(rq, rp)
方法 -
RunServer(rq, rp)
该方法中rq
封装了请求信息,rp
封装了响应信息 -
url = rq['PATH_INFO']
获取请求的url连接地址 -
ret = URLS[url]()
根据请求的url执行对应的函数 - 当我们将执行的
index()
和news()
功能函数放进Controllers
业务逻辑处理模块, 将返回结果ret
改为文件读写后的内容, 并将该文件放置到Views
或者Template
模块中, 就形成了最基础版本的MVC
和MTV
框架
接下来我们使用Tornado
实现一个简陋的任务表功能demo
**commons.css
文件内容: **
.body {
margin: 0;
background-color: cornflowerblue;
}
**index.html
文件内容: **
S1
内容展示
{% for item in contents %}
- {{item}}
{% end %}
**index.py
文件内容: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop
class MyHandle(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("index.html", contents=CONTENTS_LIST)
def post(self, *args, **kwargs):
CONTENTS_LIST.append(self.get_argument('name'))
self.render('index.html', contents=CONTENTS_LIST)
if __name__ == '__main__':
CONTENTS_LIST = []
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': 'static/',
}
application = tornado.web.Application([
(r"/index", MyHandle)
], **settings)
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
-
CONTENTS_LIST = []
为存放的是输入框输入的内容 -
settings
字典表示的是配置文件 -
'template_path': 'template'
模板文件的存放位置 -
'static_path': 'static'
静态文件的存放位置, 静态文件必须声明, 否则浏览器无法找到静态文件 -
'static_url_prefix': 'static/'
静态文件前缀, 减少每个文件引入都要加前缀的麻烦
application = tornado.web.Application([
(r"/index", MyHandle)
], **settings)
根据浏览器的url确定其对应的处理类并生成该类的对象
- `application.listen(80)` 设置服务端的监听端口
- `tornado.ioloop.IOLoop.instance().start()` 阻塞服务端进程, 等待客户端的访问
- 客户端第一次访问调用的是`MyHandle`类中的`get(self, *args, **kwargs)`方法, 服务端向客户端返回`index.html`文件
- 客户端浏览器接受到`index.html`文件之后, 在输入框中输入内容并提交之后会调用`post(self, *args, **kwargs)`, 并将输入的内容追加到
- `self.get_argument('name')` 获取指定参数的内容
`CONTENTS_LIST`中, 服务端返回`index.html`, 返回过程中`Toranado`
会将`CONTENTS_LIST` 的内容渲染到`index.html`之后才会发给客户端浏览器
**python中的模板引擎本质上是将`html`文件转换成一段`python`函数字符串, 再通过`compile`和`exec`将该函数执行, 以此来进行模板渲染**
*现在我们介绍一下模板引擎的使用: *
![项目目录](http://upload-images.jianshu.io/upload_images/4241702-f0b7d9c757a0d228.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**`uimethod.py`文件如下: **
```python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
def test_uimethod(self):
return "uimethod"
**uimodule.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from tornado.web import UIModule
class MyClass(UIModule):
def render(self, *args, **kwargs):
return "uimodule"
**index.py
文件如下: **
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tornado.web, tornado.ioloop
import uimethod as ut
import uimodule as ud
class MyHandle(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
self.render("index.html", ag="this is ag", contents=CONTENTS_LIST)
def post(self, *args, **kwargs):
CONTENTS_LIST.append(self.get_argument('name'))
self.render('index.html', contents=CONTENTS_LIST)
if __name__ == '__main__':
CONTENTS_LIST = []
settings = {
'template_path': 'template',
'static_path': 'static',
'static_url_prefix': 'static/',
'ui_methods': ut,
'ui_modules': ud
}
application = tornado.web.Application([
(r"/index", MyHandle)
], **settings)
application.listen(80)
tornado.ioloop.IOLoop.instance().start()
**index.html
文件如下: **
S1
{{ag}}
{{test_uimethod()}}
{%module MyClass()%}
内容展示
{% for item in contents %}
- {{item}}
{% end %}
*我们看看客户端访问的结果: *
- 模板引擎中的
{{key}}
表示取key
对应的值, 当key
为函数时候执行该函数并取该函数结果. 例如index.html
文件中的
实际上取得是{{ag}}
index.py
的self.render("index.html", ag="this is ag", contents=CONTENTS_LIST)
中的参数ag
的值 -
这里执行的是自定义函数, 我们将这个自定义函数写在{{test_uimethod()}}
uimethod.py
文件中, 并且在index.py
文件中导入, 然后将index.py
文件中的settings
配置增加一行'ui_methods': ut
, 该行内容表示模板引擎可执行自定义函数 - 模板引擎中的
{%%}
可用于循环语句和条件语言以及自定义类的执行,{% for item in contents %}
此处正是用于循环遍历contents
中的内容 -
此处表示模板引擎执行自定义类, 该类的文件对应的是{%module MyClass()%}
uimodule.py
文件, 我们需要在index.py
的settings
中增加一行'ui_modules': ud
, 改行表示模板引擎可使用自定义类 - 注意, 我们将
index.html
文件引入css
的方式改为了,
static_url()
是模板引擎内置的自定义函数, 用该函数引入css
文件时候, 仅当css
文件内容发生变化时候, 浏览器才会重新缓存该css
文件
考虑到篇幅太长不容易阅读, 笔者这里将关于Tornado
框架的cookie
知识, 自定义session
的使用 路由系统,以及模板引擎高级部分放在后期文章分篇共享