sanic官方中文教程https://sanicframework.org/zh/guide/
教程不涉及模板等内容,所以用前后端分离架构,后端只返回数据,不渲染模板
架构如下
使用两个服务器软件,sanic自带的软件和nginx
sanic服务器软件,运行在5000端口,只接收本机请求,只代理后端
nginx运行在80端口,接收外部请求,同时代理前端和后端,将指定请求转发到后端所在的5000端口,改动部分如下,其他都是默认配置,没有改动
server {
listen 80;
server_name localhost;
access_log logs/host.access.log main;
#前端,页面
location / { #访问根路径,打开前端主页,不进行转发
root C:/Users/zx/Documents/pyProject/web/my_app; #设置前端文件的主目录
index index.html index.htm;
}
#后端,接口
location /api/{ #访问路径带api时,进行转发,如http://127.0.0.1/api/login, 会被转发给后端
proxy_pass http://127.0.0.1:5000/; #转发给本机5000端口
proxy_set_header Host $host;
}
1.示例文件
为便于展示所有文件放在同一个目录里
api #存放目录
----server.py #主程序
----auth.py #指定页面需要登录才能执行操作
----login.py #用户登录验证,jwt认证
----query.py #自定义文件,使用蓝图注册到主程序
前端文件
----index.html #登录页
----query.html #功能查询页
1.1 login.py,用户登录验证,创建蓝图
import jwt #注意,安装命令是pip install pyjwt,而不是pip install jwt
from sanic import text
from sanic import Blueprint #导入蓝图类
login = Blueprint("login") #实例化蓝图,login是蓝图名称,引号里的内容随便填,
@login.post("/login")
async def do_login(request): #sanic的函数都要带request参数,用于获取前端传来的数据
user = request.form.get('user') #获取表单中id为user的值
passwd = request.form.get('passwd')
if user== 'inflow' and passwd == 'inflow':
token = jwt.encode({}, request.app.config.SECRET)
return text(token) #验证通过返回token
return text('fail')
from sanic import Blueprint
login = Blueprint("login",url_prefix=‘/abc’) #创建一个蓝图实例,url__prefix参数可选,用于指定路径
如以上代码指定该参数,访问login的路径就变成了/abc/login,不添加时为/login
该文件用于验证用户登录,验证成功返回一个token,在受保护的路由上,只有验证成功才能访问数据
1.2 server.py 主程序,注册蓝图,启动程序,设置配置项,父目录
from sanic import Sanic
from login import login #从login.py文件导入创建的login蓝图对象
from query import query
#0.配置
app = Sanic(__name__)
app.config.SECRET = "KEEP_IT_SECRET_KEEP_IT_SAFE" #设置秘钥,用于jwt认证,config配置项一般放在一个本地文件中,从文件读取
app.config.SERVER_NAME = "/api" #设置顶层路径,所有的路由路径都在api下面,比如/api/login
#1.蓝图注册
app.blueprint(login) #注册login蓝图,路径为http://127.0.0.1/api/login
app.blueprint(query)
#3.执行
if __name__ =='__main__':
app.run(port=5000, debug=True, access_log=False, workers=4)
#1.端口运行在5000,2.开启debug模式,有修改时自动加载,不用重启程序,3.关闭访问日志,提高速度,日志可在nginx中设置。4.开启4个线程
#命令行执行方式,进入server.py 所在目录sanic server.app --host=0.0.0.0 --port=5000 --workers=4 --debug=True --access_log=False
#和flask一样,host设置为0000时,从其他计算机也能访问,设置为127,只有本机能访问,因为我们使用nginx做为代理,所以可以用127
1.3 auth.py 路由保护
from functools import wraps
import jwt
from sanic import text
#完全照搬官方教程,一字未改
def check_token(request):
if not request.token:
return False
try:
jwt.decode(
request.token, request.app.config.SECRET, algorithms=["HS256"]
)
except jwt.exceptions.InvalidTokenError:
return False
else:
return True
def protected(wrapped):
def decorator(f):
@wraps(f)
async def decorated_function(request, *args, **kwargs):
is_authenticated = check_token(request)
if is_authenticated:
response = await f(request, *args, **kwargs)
return response
else:
return text("You are unauthorized.", 401)
return decorated_function
return decorator(wrapped)
#这个文件照搬的官方脚本,没做任何改动,就不说了
用法:需要保护的路由,导入auth模块的protected函数
from auth import protected @protected #在函数上加一个装饰器就可以了,示例见1.41.4 query.py
from sanic import Blueprint, json
from auth import protected #导入protected
import tools, add_user_right
# 0.数据定义
query = Blueprint('query')
# 一.相关函数
@query.post("/query")
@protected #添加了protected,只有1.1 login.py文件通过登录验证才会执行下面的函数,否则返回login.py中定义的401
async def query_data(request):
name = request.form.get('name')
data = {}
if request.form.get('radio1') == 'user_info':
data = tools.find_sysuser(name)
return json({"h": data}) #返回json类型的数据
1.5index.html 用户登录表单,使用ajax
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>登录</title>
<link rel="stylesheet" type="text/css" href="../css/style.css">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/main.css" rel="stylesheet">
</head>
<body>
<link rel="stylesheet" type="text/css" href="css/style.css">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">首页</a>
<a class="navbar-brand" href="/templates/query.html">数据查询</a>
<a class="navbar-brand" href="/templates/export.html">数据导出</a>
</div>
</div>
</div>
<br>
<form action="/api/login" method="post" class="form" role="form">
<div class="form-group required"><label class="form-control-label" for="user">账号</label>
<input class="form-control" id="user" name="user" required type="text" value="">
</div>
<div class="form-group required"><label class="form-control-label" for="passwd">密码</label>
<input class="form-control" id="passwd" name="passwd" required type="password" value="">
</div>
<input class="btn btn-primary btn-md" id="submit" name="submit" type="submit" value="登录">
</form> <!-- 表单 -->
</body>
action中定义了表单的提交路径/api/login .点击提交后访问http://127.0.0.1/api/lgoin , 路径含有api会转发到后端
1.6 query.html
点击查看代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>查询</title>
<link rel="stylesheet" type="text/css" href="../css/style.css">
<link href="../css/bootstrap.min.css" rel="stylesheet">
<link href="../css/main.css" rel="stylesheet">
<style>
body {
font-size: 14px;
}
label {
display: inline-block;
width: 8em;
margin-left: 0.3em;
margin-right: 0.3em;
}
input {
margin-top: 0.3em;
margin-bottom: 0.3em;
}
.tipmsg {
font-size: 14px;
color: #f00;
}
</style>
</head>
<body>
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">首页</a>
<a class="navbar-brand" href="/templates/query.html">数据查询</a>
<a class="navbar-brand" href="/templates/export.html">数据导出</a>
</div>
</div>
</div>
<div class="container">
<br />
<div>
</div>
<form id="query_form" name="query_form" method="post" class="form" role="form">
<div class="form-group row">
<label class="col-form-label col-lg-2" for="name">姓名</label>
<div class=" col-lg-10">
<input class="form-control" id="name" name="name" type="text" value="">
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-lg-2" for="phone">手机</label>
<div class=" col-lg-10">
<input class="form-control" id="phone" name="phone" type="text" value="">
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-lg-2" for="role">角色</label>
<div class=" col-lg-10">
<input class="form-control" id="role" name="role" type="text" value="">
</div>
</div>
<div class="form-group row">
<label class="col-form-label col-lg-2" for="depart">部门</label>
<div class=" col-lg-10">
<input class="form-control" id="depart" name="depart" type="text" value="">
</div>
</div>
<div>
用户信息查询<input type="radio" name="radio1" value="user_info" />
角色部门查询<input type="radio" name="radio1" value="role_depart" />
测试<input type="radio" name="radio1" value="test" />
添加角色<input type="radio" name="radio1" value="role_add" />
修改角色<input type="radio" name="radio1" value="role_mod" />
删除角色<input type="radio" name="radio1" value="role_del" />
添加用户<input type="radio" name="radio1" value="user_add" />
</div>
<div class="form-group row">
<div class="offset-lg-2col-lg-10">
<input class="btn btn-primary btn-md" id="query_button" name="query_button" type="button" value="查询"
onclick="user_query()">
</div>
</div>
</form>
<!-- 数据 -->
<div id="con"></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type=text/javascript>
function user_query(){
$.ajax({
url:'/api/query',
type:'POST',
data: $('#query_form').serialize(),
success:function(data){
show_data(data.h);
}
});
};
function show_data(data){
for(var i=0;i<data.length;i++){
var addDivDom = document.createElement('div'); // 创建div标签
var bodyDom = document.body;
bodyDom.insertBefore(addDivDom, bodyDom.lastChild); // 将addDivDom添加到body中的最后子节点中。
addDivDom.innerHTML = i + "." + data[i]; //输出
//addDivDom.id = 'id'; // div标签添加id
// addDivDom.style.color = '#fff'; // 书写style样式
//addDivDom.classList.add('classname'); // 引入css文件中的某个class样式
//$('#con').html(data[i]);
//$('#con').after(data[i]);
}
}
</script>
</body>
</html>
表单提交使用ajax异步加载,提交路径/api/query , 该路由被protectd保护,登录验证通过才能执行查询