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:客户端浏览器缓存
静态文件缓存效果图
回顾一下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转换成一个函数,并为该函数提供全局变量,然后执行该函数!!
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])}