二、修改以前的框架
1. server.py
import socket
import _thread
from request import Request
from utils import log
from routes import error
from routes.routes_todo import route_dict as todo_routes
from routes.api_todo import route_dict as todo_api
from routes.routes_weibo import route_dict as weibo_routes
from routes.routes_user import route_dict as user_routes
from routes.routes_static import route_dict as static_routes
def response_for_path(request):
r = {}
r.update(todo_api())
r.update(todo_routes())
r.update(weibo_routes())
r.update(user_routes())
r.update(static_routes())
response = r.get(request.path, error)
return response(request)
def process_request(connection):
r = connection.recv(1024)
r = r.decode()
# 把原始请求数据传给 Request 对象
request = Request(r)
# 用 response_for_path 函数来得到 path 对应的响应内容
response = response_for_path(request)
if 'static' in request.path:
log("response for static size: {}".format(len(response)))
else:
log("response log:\n{}".format(response.decode()))
connection.sendall(response)
connection.close()
def run(host, port):
# 初始化 socket 套路
# 使用 with 可以保证程序中断的时候正确关闭 socket 释放占用的端口
log('开始运行于', '{}:{}'.format(host, port))
with socket.socket() as s:
# 使用 下面这句 可以保证程序重启后使用原有端口, 原因忽略
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((host, port))
s.listen(5)
while True:
connection, address = s.accept()
# 第二个参数类型必须是 tuple
_thread.start_new_thread(process_request, (connection,))
if __name__ == '__main__':
config = dict(
host='127.0.0.1',
port=3000,
)
run(**config)
改为:
from flask import(
Flask,
)
from routes.routes_user import user_view
from routes.routes_static import static_view
if __name__ == '__main__':
app = Flask(__name__)
app.register_blueprint(user_view)
app.register_blueprint(static_view)
config = dict(
debug = True,
host='127.0.0.1',
port=8000,
)
app.run(**config)
run函数已经封装好了,路由由Blueprint
处理。这个脚本就两个功能,启动服务器和分发路由。
知识点:
- Blueprint 的使用,记住就好。
2.主页视图
from routes import current_user, login_required
from routes import http_response
from utils import template, log
def route_index(request):
u = current_user(request)
log('current user', u)
body = template('index.html', username=u.username)
return http_response(body)
def route_static(request):
filename = request.query.get('file', )
path = 'static/' + filename
with open(path, 'rb') as f:
header = b'HTTP/1.1 200 OK\r\n\r\n'
binary = header + f.read()
return binary
def route_dict():
r = {
'/': login_required(route_index),
'/static': login_required(route_static),
}
return r
改为:
from flask import (Blueprint,render_template,send_from_directory,request,)
from routes import current_user, login_required
from utils import log
static_view = Blueprint('static_view', __name__)
# 先处理路由函数,如果没注册,再重定向到登录界面
@static_view.route('/')
@login_required
def route_index():
u = current_user()
return render_template('index.html', username=u.username)
@static_view.route('/static')
def route_static():
filename = request.args.get('file', None)
return send_from_directory('static/' , filename)
知识点:
- 权限函数在路由函数之下,原因是先找到路由,发现没登录,再执行权限函数
-
Blueprint
的使用
-flask.send_from_directory(dictionary, filename)
就是读取文件,两个参数,一个是路径,一个是文件名;
current_user()函数也需要更改
def current_user(request):
session_id = request.cookies.get('sid', '')
sessions = Session.all()
for s in sessions:
if s.session_id == session_id:
u = User.find_by(id=s.user_id)
return u
return None
# 改为:去掉request参数,在脚本调用flask.request,
# cookies也是通过POST进行传递的,在body里面;
def current_user():
session_id = request.cookies.get('sid', '')
sessions = Session.all()
for s in sessions:
if s.session_id == session_id:
u = User.find_by(id=s.user_id)
return u
return None
3.登录和注册视图:
from models.session import Session
from routes import (random_str,redirect,http_response)
from utils import log
from utils import template
from models.user import User
def route_login(request):
log('login, cookies', request.cookies)
if request.method == 'POST':
form = request.form()
u = User(form)
if u.validate_login():
session_id = random_str()
u = User.find_by(username=u.username)
s = Session.new(dict(
session_id=session_id,
user_id=u.id,
))
log('session', s)
headers = {
'Set-Cookie': 'sid={}'.format(session_id)
}
# 登录后定向到 /
return redirect('/', headers)
# 显示登录页面
body = template('login.html')
return http_response(body)
def route_register(request):
if request.method == 'POST':
form = request.form()
u = User.new(form)
if u.validate_register():
# 注册成功再保存
u.save()
# 注册成功后 定向到登录页面
return redirect('/login')
else:
# 注册失败 定向到注册页面
return redirect('/register')
# 显示注册页面
body = template('register.html')
return http_response(body)
def route_dict():
r = {
'/login': route_login,
'/register': route_register,
}
return r
改为:
from models.session import Session
from flask import (Blueprint,render_template,redirect,request,make_response,)
from routes import random_str
from utils import log
from models.user import User
user_view = Blueprint('user_view', __name__)
@user_view.route('/login', methods = ['POST', 'GET'])
def route_login():
log('login, cookies', request.cookies)
if request.method == 'POST':
form = request.form
u = User(form)
if u.validate_login():
session_id = random_str()
u = User.find_by(username=u.username)
s = Session.new(dict(
session_id=session_id,
user_id=u.id,
))
log('session', s)
# 登录后定向到 /
redirect_to_index = redirect('/')
response = make_response(redirect_to_index)
response.set_cookie('sid', value=session_id)
return response
# 显示登录页面
return render_template('login.html')
@user_view.route('/register', methods = ['POST', 'GET'])
def route_register():
if request.method == 'POST':
form = request.form
u = User.new(form)
if u.validate_register():
# 注册成功再保存
u.save()
# 注册成功后 定向到登录页面
return redirect('/login')
else:
# 注册失败 定向到注册页面
return redirect('/register')
# 显示注册页面
return render_template('register.html')
- request为全局变量,form为属性。template改为render_template()
- 主要是cookies设置的改变
redirect_to_index = redirect('/')
response = make_response(redirect_to_index)
response.set_cookie('sid', value=session_id)
return response
做出了两个响应,一个是重定向到index
页面,一个是响应一个cookies
;
在登录成功之后,不仅要返回重定向,还要返回cookies
;
- 但是返回重定向为什么要用
make_response()
呢?
因为return的内容,浏览器会默认为body
,而cookies
储存在header
中。但是我们没有办法更改响应结构,就只能去创建一个请求结构了。构造一个response
需要基于某种情况,这种是重定向,还可以是渲染页面(response = make_response(render_template('',''))
)。 - 对
cookies
的再理解:
cookies
:cookies
是保存在客户端的,当用户发送登录信息给服务器的时候,服务器将用户名存在请求头发送给浏览器,这样,浏览器以后的请求中都包含了用户名这个字段,服务器就可以通过这个字段辨别用户了。
sessions
:sessions
是保存在服务器的,在服务器端生成的,是依赖于cookies
的,只不过在cookies
的外面套上了一件衣服,将重要信息掩盖。客户将登陆信息发送给服务器,服务器生成一个随机字符串赋值于session_id
,构架一个session_id
和用户名两个字段的字典,一一对应,并将session_id
发送给客户端。这样服务器就可以通过客户端发过来的session_id
来辨别用户了。
客户端每次登陆,都会生成一个随机字符串,服务器和客户端通过这个进行识别。下次相同用户登陆的时候,又会生成其他随机字符串。
login_required也需要修改:
def login_required(route_function):
"""
def f(request):
u = current_user(request)
if u is None:
return redirect('/login')
else:
return route_function(request)
return f
改为:
def login_required(route_function):
def f():
u = current_user()
if u is None:
return redirect('/login')
else:
return route_function()
return f
4.todo首页视图
from models.todo import Todo
from routes import (
redirect,
http_response,
current_user,
login_required,
)
from utils import template, log
def index(request):
u = current_user(request)
body = template('todo_index.html')
return http_response(body)
def route_dict():
d = {
'/todo/index': login_required(index),
}
return d
改为:
from flask import (render_template, Blueprint,)
from routes import login_required
todo_view = Blueprint('todo_vies', __name__)
@todo_view.route('/todo/index')
@login_required
def index():
return render_template('todo_index.html')
5.api_todo
from routes import json_response
from models.todo import Todo
# 本文件只返回 json 格式的数据
# 而不是 html 格式的数据
def all(request):
todos = Todo.all_json()
return json_response(todos)
def add(request):
# 得到浏览器发送的表单, 浏览器用 ajax 发送 json 格式的数据过来
# 所以这里我们用新增加的 json 函数来获取格式化后的 json 数据
form = request.json()
# 创建一个 todo
t = Todo.new(form)
# 把创建好的 todo 返回给浏览器
return json_response(t.json())
def delete(request):
todo_id = int(request.query.get('id'))
t = Todo.delete(todo_id)
return json_response(t.json())
def update(request):
form = request.json()
log('api todo update', form)
todo_id = int(form.get('id'))
t = Todo.update(todo_id, form)
return json_response(t.json())
def route_dict():
d = {
'/api/todo/all': all,
'/api/todo/add': add,
'/api/todo/delete': delete,
'/api/todo/update': update,
}
return d
改为
from flask import (request, Blueprint, jsonify)
from routes import login_required
from utils import log
from models.todo import Todo
api_todo_view = Blueprint('api_todo_view', __name__)
# 本文件只返回 json 格式的数据
# 而不是 html 格式的数据
@api_todo_view.route('/api/todo/all')
@login_required
def api_all():
todos = Todo.all_json()
return jsonify(todos)
@api_todo_view.route('/api/todo/add', methods = ['POST'])
@login_required
def api_add():
# 得到浏览器发送的表单, 浏览器用 ajax 发送 json 格式的数据过来
# 所以这里我们用新增加的 json 函数来获取格式化后的 json 数据
form = request.json
# 创建一个 todo
t = Todo.new(form)
# 把创建好的 todo 返回给浏览器
return jsonify(t.json())
@api_todo_view.route('/api/todo/delete')
@login_required
def api_delete():
todo_id = int(request.args.get('id', None))
t = Todo.delete(todo_id)
return jsonify(t.json())
@api_todo_view.route('/api/todo/update', methods = ['POST'])
@login_required
def api_update():
form = request.json
log('api todo update', form)
todo_id = int(form.get('id'))
t = Todo.update(todo_id, form)
return jsonify(t.json())
login_required()函数:
from functools import wraps
def login_required(route_function):
@wraps(route_function)
def f():
u = current_user()
if u is None:
log('非登录用户')
return redirect('/login')
else:
return route_function()
return f
- 自己写的weibo程序
# 用于评论的更新:评论的user_id与登录的id一直
def comment_same_user_required(route_function):
@wraps(route_function)
def f():
u = current_user()
if request.method == 'GET':
comment_id = int(request.args.get('id', None))
else:
form = request.json
comment_id = form.get('id', None)
c = Comment.find(int(comment_id))
if u.id == c.user_id:
log('权限用户')
return route_function()
else:
return redirect('/webo/index')
return f
这里注意一点,由于ajax
发过来的数据是json
格式,所以form
数据需要通过request.json
来接收,二GET
请求发过来的数据在url
里面,不是json
格式,所以可以直接取。ajax(method,path,form,callback)
三个知识点:
- request.json
- jsonify()
- from functools import wraps @wraps(参数为母函数参数)
http://www.w3school.com.cn/json/
http://blog.csdn.net/hqzxsc2006/article/details/50337865
https://www.tuicool.com/articles/NzEbqmj
【13-2(全)】
网友评论