Bottle学习笔记
笔记放在仓库:https://github.com/Turing-dz/bottle_study_notes
1.环境
使用管理员身份打开cmd
conda create -n bottle python
conda activate bottle
pip install bottle
2.知识点
2.1 框架
1.最少代码的服务器程序
from bottle import run,route
@route('/')
def index():
return "I am bottle"
run(host="localhost",port="80",debug=True,deloader=True)
2.使用面向对象的服务器程序框架
from bottle import Bottle,run
web=Bottle()
@web.route("/")
def index():
return "I am bottle2"
run(web)
服务器运行函数run,指定服务器的运行IP和端口(host,port);显示调试信息(debug);代码修改后自动重启服务(deloader)
2.2 URL装饰器
1.默认响应get请求,可以定义method响应多种请求方法,request.method判断请求方式
from bottle import route, run ,request,get,post
htmlstr="""
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BOTTLE</title>
</head>
<body>
<form action ="/demo_gp" method="POST">
<input type="submit"/>
</form>
</body>
</html>
"""
@route("/")
def index():
return htmlstr
@route("/demo_post",method="POST")#1.响应post请求
def pt():
return "receive method post"
#@route("/demo_gp",method=["POST","GET"])#2.响应两种请求方式
@get("/demo_gp")#3.响应两种请求方法
@post("/demo_gp")
def gp():
if request.method=="GET":#request.method判断请求的方式
return htmlstr
else:
return "this is post"
run()
2.动态路由
from bottle import route,run ,Bottle
import re
@route("/hello/<name>")#1.路由传递字符串
def index(name=""):
return "Hello %s"%name
@route("/uid/<uid:int>")#2.路由传递整型
def getuid(uid=0):
return "your id is %d"%uid
@route("/weight/<weight:float>")#3.路由传递浮点数
def getweight(weight):
return "your weight:%f"%weight
@route("/getfilepath/<file_path:path>")#4.路由传递路径
def getpath(file_path):
return file_path
@route("/rege/<ret:re:[0-1]*>")#5.正则匹配路由传递的参数
def rege(ret):
return ret
#6.自定义路由过滤器
app=Bottle()
def list_filter(config):
delimiter=config or ","
regexp=r'\d+(%s\d)*'%re.escape(delimiter)
def to_python(match):
return map(int,match.split(delimiter))
def to_url(numbers):
return delimiter.join(map(str,numbers))
return regexp,to_python,to_url
app.router.add_filter('idslist',list_filter)
@app.route("/hello/<ids:idslist>")
def hello(ids):
res=''
for i in ids:
res+=str(i)+'-'
return "hello %s"%res
run(app,port=80,debug=True)
2.3 404与url转向
from bottle import route,run,static_file,error,abort,redirect
#1.返回静态文件内容return static_file(filename,root="静态文件地址",mimetype="静态文件类型")
@route("/static/<filename:path>")
def index(filename):
return static_file(filename,root="",download=True)
#2.强制下载文件,return static_file(filename,root="",download=True/"yourfilename")
@route("/dl/<filename:path>")
def dl(filename):
return static_file(filename,root='',download="sd.jpg")
#3.指定404页面
@error(404)
def err(err):
return "页面丢失"
#4.url转向:转向错误abort(404,"error_info"),调用redirect("other url")
@route("/abort")
def ab():
abort(404,"转向丢失页面")##4.1转向丢失页面
##4.2转向指定页面
@route("/")
def index():
return "登录成功"
@route("/login/<name>")###登陆成功转向/目录,失败就转向login,且页面丢失
def login(name):
if name =="abc":
redirect("/")
else:
redirect("/login")
run(port="80",debug=True,reloader=True)
2.4获取get与post请求参数
1.get参数提供:url链接添加”?名称=值&名称=值…“
get 参数获取:request.query.name(当name不存在时,返回空字符串)
from bottle import route, run ,request,get,post
htmlstr="""
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BOTTLE</title>
</head>
<body>
<form action ="/get_to">
<label>姓名</label>
<input type="text" name="name"/>
<label>电话号码</label>
<input type="text" name="telephone"/>
<label>邮箱</label>
<input type="text" name="email"/>
<input type="submit"/>
</form>
<a href="http://127.0.0.1:8080/get_to?name=1&telephone=2&email=3">提交get信息</a>
</body>
</html>
"""
@route("/")
def index():
return htmlstr
@route("/get_to")
def getto():
name=request.query.name#获取
telephone=request.query.telephone
email=request.query.email
return (name,telephone,email)
run(debug=True,reloader=True)
2.post参数提供:在提交的表单中添加method=“post”
post 参数获取:request.forms.get(“name”)
from bottle import route, run ,request,get,post
htmlstr="""
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BOTTLE</title>
</head>
<body>
<form action ="/post_to" method="POST">
<label>姓名</label>
<input type="text" name="name"/>
<label>电话号码</label>
<input type="text" name="telephone"/>
<label>邮箱</label>
<input type="text" name="email"/>
<input type="submit"/>
</form>
</body>
</html>
"""
@route("/")
def index2():
return htmlstr
@route("/post_to",method="POST")#post方式接受
def postto():
name=request.forms.get('name')#post方式提取参数
telephone=request.forms.get('telephone')
email=request.forms.get('email')
return (name,telephone,email)
run(debug=True,reloader=True)
2.5为web客户端返回不同类型数据
from bottle import route,run,Response
@route("/")
def index():
#return {"a":"djn","b":45,"c":2.3}#1.返回字典
#return ()#2.返回空
# dec="abcd"
# return dec.encode("utf-8") #3.返回字节串
#return ("sdf","sdf")4.#返回元组或列表,不能返回嵌套的
#5.Response.charset或者Response.content_type指定返回字节编码
#Response.charset="gbk"
#Response.content_type="text/html;charset=gbk"
return "你好"
run(port="80",debug=True,reloader=True)
2.6使用cookie与signed cookie跟踪客户
from bottle import route,run,response,request
#1.添加cookie, response.set_cookie("name","value")
@route("/")
def index():
response.set_cookie("zoe","27")
return "set cookie!"
#2.获取cookie,request.get_cookie("name")
@route("/get_cookie")
def getcookie():
return request.get_cookie("zoe")
#3.添加签名的cookie,response.set_cookie("name","value",secret="")
@route("/setsign")
def setsign():
response.set_cookie("dz","28",secret="sfkjskfjdkfkls")
return "signed cookie"
#4.获取cookie,request.get_cookie("name",secret="")
@route("/getsign")
def getsign():
return request.get_cookie("dz",secret="sfkjskfjdkfkls")
#5.普通cookie中有中文时,使用urllib.parse.quote编码,urllib.parse.unquote解码;signed_cookie不用设置
from urllib.parse import quote,unquote
@route("/chain_set")
def ch_set():
response.set_cookie("myname",quote("我是"))
return "中文cookie"
@route("/chain_get")
def ch_get():
return unquote(request.get_cookie("myname"))
run(port="80",debug=True,reloader=True)
2.7HTTP上传接收文件
#1.实现上传文件页面<form action ="" method="POST" enctype="multipart/form-data">,<input type="file" name="fileupload"/>,<input type="submit" value="上传文件"/>
from bottle import route,run ,request
htmlstr="""
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>BOTTLE</title>
</head>
<body>
<form action ="/get_file" method="POST" enctype="multipart/form-data">
<input type="file" name="fileupload"/>
<input type="submit" value="上传文件"/>
</form>
</body>
</html>
"""
@route("/")
def index():
return htmlstr
#2.实现接受上传文件业务的函数,request.files.get("name")
@route("/get_file",method=["GET","POST"])
def getfile():
if request.method=="GET":
return htmlstr
else:
uf=request.files.get("fileupload")
uf.save(uf.filename)
return "接受到了文件,并保存到了本地根目录"
run(port="80",debug=True,reloader=True)
2.8内建模板引擎
#1.嵌入python变量from bottle import template;template("{{name}});template("Str with{{a}} many{{b}} values",**adict)
from bottle import route,run,template
html="""
begin:{{name}}, {{age}}, {{weight}}, {{courses}}, {{mytest.disp()}},{{!html_str}}:end!
% if MyScores:
<table>
<tr>
<td>subject</td>
<td>score</td>
</tr>
% for (sub,score) in MyScores.items():
<tr>
<td>{{sub}}</td>
<td>{{score}}</td>
</tr>
% end
</table>
% else:
没有查询到scores
% end
"""
class MyTestClass():
def __init__(self,x=0,y=0):
self.x=x
self.y=y
def disp(self):
return (self.x,self.y)
@route("/stu/<name>")
def index(name="Stranger"):
age=12
weight=23.32
html_str="<a href='sfdnsdjfn'>链接</a>"#如何把这个字符串变量传到html时,让它转义为html呢,只需要再html的变量名前加上!
courses=["english","kero"]
mytest=MyTestClass()
#参数太多时,可以打包成字典,然后再解包
# my=dict(name=name,age=age,weight=weight,courses=courses,mytest=mytest,html_str=html_str)
# return template(html,**my)
#2嵌入python代码;%开头嵌入单行代码;<%--------%>嵌入多行python代码,
MyScores={"chinese":334,"english":23,"math":2384}
return template(html,name=name,age=age,weight=weight,courses=courses,mytest=mytest,html_str=html_str,MyScores=MyScores)
run(port="80",debug=True,reloader=True)
% include(“nav.tpl”),将nav.tpl引入到当前模板中
% rebase("base.tpl)将当前模板文件嵌入到父模板的{{!base}}中 ,其实就是引入父模板。
##父模板base.tpl,这里引入了nav.tpl文件,然后base部分由子模版去继承添加
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<body>
% include("nav.tpl")
{{!base}}
</body>
</html>
## nav.tpl文件
<div>
<a href="#">index1</a>
<a href="#">index2</a>
<a href="#">index3</a>
<a href="#">index4</a>
</div>
##html2template.tpl文件,这里继承了父模板
% rebase("base.tpl")
begin:{{name}}, {{age}}, {{weight}}, {{courses}}, {{mytest.disp()}},{{!html_str}}:end!
% if MyScores:
<table>
<tr>
<td>subject</td>
<td>score</td>
</tr>
% for (sub,score) in MyScores.items():
<tr>
<td>{{sub}}</td>
<td>{{score}}</td>
</tr>
% end
</table>
% else:
没有查询到scores
% end
模板中变量管理函数:define(name),get(name,default=None),setdefault=(name=value)
当return函数没有传递参数时,变量一定要有default值
%setdefault("variable","dnfjk")
{{variable}}
拿到变量variable传递的参数,若无就取no字符串为默认值。
{{get("variable","no")}}
2.9Bottle网站程序的部署
使用waitress同步框架或tornado异步框架,使用nginx进行反向代理。
文件目录如下:
文件目录如下
-nginx
-static
----img
----css
----js
-template
manage.py
对nginx的conf\nginx.conf文件修改内容:
#111111111111111111111111.指明nginx所代理的后端内容,就是本机上的8088端口启动的文件
upstream myserver{
ip_hash;
server localhost:8088;
server localhost:8081;
server localhost:8082;
}
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#222222222222222222.在server函数中,写明自己的代理地址,就是1里面设置的名称myserver
location / {
root html;
index index.html index.htm;
proxy_pass http://myserver;
}
#3333333333333333333333.将静态文件映射到指定的位置
location ~ \.(jpg|jpeg|png|gif)$ {
root C:/Users/dz/Desktop/bottle/static/img;
}
location ~ \.js$ {
root C:/Users/dz/Desktop/bottle/static/js;
}
location ~ \.css$ {
root C:/Users/dz/Desktop/bottle/static/css;
}
测试nginx配置修改成功
nginx -t
nginx: the configuration file C:\Users\dz\Desktop\bottle\nginx-1.8.0/conf/nginx.conf syntax is ok
nginx: configuration file C:\Users\dz\Desktop\bottle\nginx-1.8.0/conf/nginx.conf test is successful
启动nginx
nginx
#1.使用waitress同步框架部署后端:pip install waitress;tornado 是异步框架;nginx后端反向代理
from bottle import route,run,template
import sys
@route("/")
def index():
return template("templates/index.html")
port="8088"
if len(sys.argv)==2:
port=sys.argv[1]
# run(server="waitress",port=port,debug=True,reloader=True)
run(server="tornado",port=port,debug=True,reloader=True)