[py]pyweb框架本质-tornado框架初探

py实现服务器

先看下web框架本质

  • web框架=wsgi+业务逻辑处理
  • 有些web框架自带 wsgi+业务逻辑处理
  • 有些框架只有 业务逻辑处理,跑起来需要借助第三方符合wsgi规范的webserver

写一个简单的webserver

py中wsgiref模块实现了wsgi.但在py2.x好使.py3.x不好用.

代码逻辑

#!/usr/bin/env python
# coding=utf-8

from wsgiref.simple_server import make_server

def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ["PATH_INFO"]

    if url=="/new":
        msg = "new"
    elif url=="/bbs":
        msg="bbs"
    else:
        msg="404"
    return msg

if __name__ == '__main__':
    httpd = make_server('', 8003, RunServer)
    print("Serving HTTP on port 8003...")
    httpd.serve_forever()

效果

进一步优化,目的:不修改Runserver逻辑.

#!/usr/bin/env python
# coding=utf-8

from wsgiref.simple_server import make_server


def new():
    return "new"


def bbs():
    return "bbs"


urls = {
    "/new": new,
    "/bbs": bbs,
}


def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ["PATH_INFO"]

    if url in urls.keys():
        func_name = urls[url]
        ret = func_name()
    else:
        ret = "404"
    return ret


if __name__ == '__main__':
    httpd = make_server('', 8003, RunServer)
    print("Serving HTTP on port 8003...")
    httpd.serve_forever()

列表试验:

  • for i in alis.key()
  • for i in alis.value()
  • for i in []: 判断是否在列表中

实现类似框架思想

#!/usr/bin/env python
# coding=utf-8

def show():
    return "show info"


def new():
    return "new info"


urls = {
    "/new": new,
    "/bbs": show,
}


def Runserver(url):
    if url in urls.keys():
        fun_name = urls[url]
        return fun_name()
    else:
        return "404"


print Runserver("/new")

毛台框架实现–实现mvc拆分

└─maotai
    │  01new.html
    │  controller.py
    │  controller.pyc
    │  start.py
    │  urls.py
    │  urls.pyc
    │  views.py
    │  __init__.py
    │
    └─views

controller.py逻辑处理

C:\Users\Administrator\Desktop\py\maotai>type controller.py

#!/usr/bin/env python
# coding=utf-8

def new():
    # return "new"
    f=open("./01new.html",'r')
    data = f.read()
    f.close()
    return data

def bbs():
    return "bbs"

urls.py url路由器

C:\Users\Administrator\Desktop\py\maotai>type urls.py

#!/usr/bin/env python
# coding=utf-8

import controller
urls={
    "/new":controller.new,
    "/bbs":controller.bbs,
}

start.py wsgi服务器

C:\Users\Administrator\Desktop\py\maotai>type start.py

#!/usr/bin/env python
# coding=utf-8

from wsgiref.simple_server import make_server
from urls import urls
def RunServer(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/html')])
    url = environ["PATH_INFO"]
    if url in urls.keys():
        msg=urls[url]()
    else:
        msg="404"
    return "<h1>"+msg+"</h1>"


if __name__ == '__main__':
    httpd = make_server('', 8003, RunServer)
    print("Serving HTTP on port 8000...")
    httpd.serve_forever()

小结:
- wsgi接待请求start.py–>根据请求url分发到路由系统urls.py–>路由分发到各个业务逻辑层controller.py

实现template阶段一: 动态获取数据-显示时间

controller.py 业务逻辑层

import os
import time
...

def bbs():
    f=open(os.path.join("views","bbs.html"),'r') ## 这里是路径的jion
    data = f.read()
    f.close()
    data=data.replace("{{item}}", str(time.time()))
    return data

bbs.html 模板

bbs.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<h1>now: {{item}}</h1>
<div class="box">this is bbs</div>
</body>
</html>

实现template阶段二:配合jinj2

pip install jinja2
/root/.pip/pip.conf

[global]
index-url = http://mirrors.aliyun.com/pypi/simple/

[install]
trusted-host=mirrors.aliyun.com

contorller.py逻辑处理

# contorller.py

def bbs():
    f = open(os.path.join("views", "bbs.html"), 'r')
    data = f.read()
    f.close()
    tmp=Template(data)
    data=tmp.render(name="maotai",user_list=['aaron','bob','cristin','danny'])
    return data.encode("utf-8")

bbs.html template模板

在模板中实现了数据的逻辑处理

bbs.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<h1>name: {{ name }}</h1>
<ul>
    {% for user in user_list %}
    <li>{{ user }}</li>
    {% endfor %}
</ul>
</body>
</html>

tornado

参考:
武老师tornado快速入门

tornado返回字符串

这里我用py3

self.write

pyweb.py

#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web


# controller 业务逻辑处理
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


# urls 路由系统 路由映射
application = tornado.web.Application([
    (r"/index", MainHandler),
])

# wsgi 服务器
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

tornado返回html

controller.py

self.render

pyweb.py

#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("bbs.html")

application = tornado.web.Application([
    (r"/index", MainHandler),
])

if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

template模板 bbs.html

# bbs.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<div class="box"></div>
</ul>
</body>
</html>

tornano使用setting设置绝对路径

  • 实现template路径的配置
├── template
│   └── bb.html
└── tornado_demo.py

业务模块tornado_demo.py

C:\Users\Administrator\Desktop\py\d2\tornado>type tornado_demo.py
#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web

# 业务逻辑处理模块
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html")

    def post(self):
        self.write("hello post")



# 配置选项模块
settings = {
    "template_path":"template", #模板路径配置
}


# 路由模块
application = tornado.web.Application([
    (r"/index", MainHandler),],
    ** settings
)


## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

template模块 template/bb.html

C:\Users\Administrator\Desktop\py\d2\tornado>type template/bb.html

# template/bbs.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <style>
        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<div class="box"></div>
</ul>
</body>
</html>

tornado静态路径配置

  • 存放css js等. 该框架需要对这些文件做路径配置,不然html找不到js等.

新建static,将common.css存放在其下

├── static
│   └── common.css
├── template
│   └── bb.html
└── tornado_demo.py

配置选项

settings = {
    "template_path":"template",
    "static_path":"static",
}

template搜索js/css文件

前端会渲染成这样的路径static/common.css

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <link rel="stylesheet" href="static/common.css"> //这里就可以找到了
    <style>

        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<div class="box"></div>
</ul>
</body>
</html>

静态文件设置前缀和静态文件缓存

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': 'sss', ## 像是static_path的一个别名
}

前端会渲染成这样的路径sss/common.css

<link rel="stylesheet" href="sss/common.css">

前端修改真正意义的静态文件路径

<link href="{{static_url("commons.css")}}" rel="stylesheet" />

前端渲染效果:

<link rel="stylesheet" href="/sss/common.css?v=a147e9674a95a28fc274cb8e1f4c94c2">

后面的v=xx做缓存用.当css发生变动时,这里才重新请求css文件.

一份完整的code

tonordo_demo.py

#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web

# 业务逻辑处理模块
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html")

    def post(self):
        self.write("hello post")



# 配置选项模块
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/sss/',
}


# 路由模块
application = tornado.web.Application([
    (r"/index", MainHandler),],
    ** settings
)


## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

static/common.css

body{
    background-color: #aaa;
}

template/bbs.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <link rel="stylesheet" href="{{static_url("common.css")}}">
    <!--<link rel="stylesheet" href="/sss/static/common.css">-->
    <style>

        .box{
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
<div class="box"></div>

</body>
</html>

传数据到template–表单提交

实现简单效果:

逻辑如下图:

tornado逻辑处理模块

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html")

    def post(self,*args,**kwargs):
        print "post"
        self.write("hello post")

template表单

<body>
    <form  method="post" action="/index">
        <input type="text">
        <input type="submit" value="提交">
    </form>
</body>

完整的code

tornado模块

#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web

# 业务逻辑处理模块
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html")

    def post(self,*args,**kwargs):
        print("post")
        self.write("hello post")



# 配置选项模块
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/sss/',
}


# 路由模块
application = tornado.web.Application([
    (r"/index", MainHandler),],
    ** settings
)


## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()

template/bbs.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>bbs</title>
    <link rel="stylesheet" href="{{static_url("common.css")}}">
    <!--<link rel="stylesheet" href="/sss/static/common.css">-->
    <style>

        .box {
            width: 200px;
            height: 200px;
            background-color: pink;
        }
    </style>
</head>
<body>
    <form  method="post" action="/index">
        <input type="text" name="name">
        <input type="submit" value="提交">
    </form>
</body>
</html>

传数据到template–展示数据-静态数组

将静态数据传到template展示

    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html",names=[1,2,3,4,5])

    def post(self,*args,**kwargs):
        name = self.get_argument("name")
        INPUT_LIST.append(name)
        self.write("hello post")

template循环遍历数组

<h1>添加数据</h1>
<form  method="post" action="/index">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>展示数据</h1>
    {% for name in names %}
    <li>{{ name }}</li>
    {% end %}
</ul>

传数据到template–展示数据-展示前台添加的数据

INPUT_LIST=[]
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        self.render("bbs.html",names=INPUT_LIST)

    def post(self,*args,**kwargs):
        name = self.get_argument("name")
        INPUT_LIST.append(name)
        self.render("bbs.html", names=INPUT_LIST)
<h1>添加数据</h1>
<form  method="post" action="/index">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>展示数据</h1>
    {% for name in names %}
    <li>{{ name }}</li>
    {% end %}
</ul>

传数据到template 通过get提交数据&展示数据

<body>
<h1>添加数据</h1>
<form  method="get" action="/index">
    <input type="text" name="username">
    <input type="text" name="password">
    <input type="submit" value="提交">
</form>
<h1>展示数据</h1>
    {% for name in names %}
    <li>{{ name }}</li>
    {% end %}
</ul>
</body>

完整的code:

#!/usr/bin/env python
# coding=utf-8

import tornado.ioloop
import tornado.web

INPUT_LIST=[]

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        name = self.get_argument("name")
        INPUT_LIST.append(name)
        # self.write("Hello, world")
        self.render("bbs.html",names=INPUT_LIST)

# 配置选项模块
settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/sss/',
}


# 路由模块
application = tornado.web.Application([
    (r"/index", MainHandler),],
    ** settings
)


## wsgi模块
if __name__ == "__main__":
    application.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
<body>
<h1>添加数据</h1>
<form  method="get" action="/index">
    <input type="text" name="name">
    <input type="submit" value="提交">
</form>
<h1>展示数据</h1>
    {% for name in names %}
    <li>{{ name }}</li>
    {% end %}

通过浏览器提交数据

# 通过浏览器提交数据
http://127.0.0.1:8888/index?username=123&password=123456

通过以下代码,可以打印出提交的数据

INPUT_LIST=[]
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        print self.get_argument("username")
        print self.get_argument("password")
        self.render("bbs.html",names=INPUT_LIST)

通过get方法实现数据的提交和展示

INPUT_LIST=[]
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        # self.write("Hello, world")
        username= self.get_argument("username")
        password= self.get_argument("password")
        INPUT_LIST.append(username)
        INPUT_LIST.append(password)
        self.render("bbs.html",names=INPUT_LIST)
        # self.render("bbs.html")

一个小的注意事项:
如果不加参数?username=123&password=456直接访问:http://127.0.0.1:8888/index
会报错:

WARNING:tornado.general:400 GET /index (127.0.0.1): Missing argument username
WARNING:tornado.access:400 GET /index (127.0.0.1) 1.00ms
    def get(self):
        username= self.get_argument("username",None)
        password= self.get_argument("password",None)
        if username and password: # 如果不为空才添加
            INPUT_LIST.append(username)
            INPUT_LIST.append(password)
        self.render("bbs.html",names=INPUT_LIST)

向前端传值

    def get(self):
        # self.write("Hello, world")
        username= self.get_argument("username",None)
        password= self.get_argument("password",None)
        if username and password:
            INPUT_LIST.append(username)
            INPUT_LIST.append(password)
        self.render("bbs.html",npm="NPM",names=INPUT_LIST)
<h1>{{ npm }}</h1>

模板判断语句

<h1>展示数据</h1>
    {% for name in names %}
        {% if name == "maotai" %}
            <li style="color:yellow">{{ name }}</li>
        {% else %}
            <li>{{ name }}</li>
        {% end %}
    {% end %}
</ul>

模板函数uimethod

添加uimethod.py

def func(self,arg):
    return arg.lower()

设置主程序关联函数

import uimethod as mt

settings = {
    "template_path":"template",
    "static_path":"static",
    "ui_methods": mt,
}

模板里调用

<h1>{{func(npm)}}</h1>

模板函数uimodule

新建uimodule.py

from tornado.web import UIModule
from tornado import escape

class custom(UIModule):

    def render(self, *args, **kwargs):
        return "12345"
import uimodule as md

settings = {
    "template_path":"template",
    "static_path":"static",
    "ui_methods": mt,
    "ui_modules": md,
}
<h3>{% module custom() %}</h3>

模板语法小结:

模板语言支持
+ {{}}
+ {%%}
+ 自定义方式 uimethod uimodule

tornado内置函数

Tornado默认提供的这些功能其实本质上就是 UIMethod 和 UIModule

escape: tornado.escape.xhtml_escape 的別名
xhtml_escape: tornado.escape.xhtml_escape 的別名
url_escape: tornado.escape.url_escape 的別名
json_encode: tornado.escape.json_encode 的別名
squeeze: tornado.escape.squeeze 的別名
linkify: tornado.escape.linkify 的別名
datetime: Python 的 datetime 模组
handler: 当前的 RequestHandler 对象
request: handler.request 的別名
current_user: handler.current_user 的別名
locale: handler.locale 的別名
_: handler.locale.translate 的別名
static_url: for handler.static_url 的別名
xsrf_form_html: handler.xsrf_form_html 的別名

带escape 是转义的意思,涉及到xss攻击

handler.request == self.request 相当于uimethod的

内置函数使用实例–static url:客户端浏览器缓存

静态文件缓存效果图
image

回顾一下static_url_prefix

settings = {
    'template_path': 'template',
    'static_path': 'static',
    'static_url_prefix': '/sss/', # 如果使用了static_url,这里会自动添加, 如果没用static_url_prefix,在调用css时候,需要手动在html里prefix这个,如``` <style rel="stylesheet" href='sss/static/common.css'></style>```
}

前端页面调用 static_url(),做静态缓存

<style rel="stylesheet" href='{{ static_url("common.css") }}'></style>
<script src='{{ static_url("maotai.js")}}'></script>

类比下刚刚用到的uimethod用法<h1>{{func(npm)}}</h1>

模板引擎的实质

整个过程其实就是将一个html转换成一个函数,并为该函数提供全局变量,然后执行该函数!!
image

test.py

#!/usr/bin/env python
# coding=utf-8

namespace= {"name":"maotai","data":[11,22,33]}
code = '''def hellocode():return "name %s,age %d"%(name,data[0])'''

func = compile(code,'<string>',"exec")

exec(func,namespace)

result=namespace['hellocode']()
print (result)

简言之理解:

通过namespace执行函数,而在执行函数前,函数所使用的全局变量已经定义了.

namespace= {"name":"maotai","data":[11,22,33],'hellocode':def hellocode():return "name %s,age %d"%(name,data[0])}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值