重构查分后台
背景
几个月前有写过一个简单的快速查成绩的网站,放在了我的 GitHub 里。
凑和能用,但是一直有一个疙瘩:查成绩的 API 太丑!
这是当时的 get_grade.php
,点击查询按钮会向它发送 POST 请求,由它来返回结果。没错,get_grade.php
竟然是直接在服务器端执行命令获得输出!(我们就先不谈它的安全风险了,😫😶😫🤐😖😰🤢)
<?php
$username = $_POST['username'];
$password = $_POST['password'];
$location = '/.../get_grade.py';
$command = 'python3 ' . $location . ' ' . $username . ' ' . $password;
$new_command = escapeshellcmd($command);
$output = shell_exec($new_command);
echo json_encode($output);
既然脚本是用 Python 写的,为什么当时不使用 Flask 做一个查成绩的 API 呢?
没有采用 Flask 搭建接收
POST
请求的后端服务,因为我自己的前端网站是 Https 加密的,它不允许从已加密的前端网站从未加密的后端接口获取信息,即不允许 Http/Https 混用。而在配置 Flask 的 Https 加密的过程中我遇到了困难,遂放弃。
重构
后来,我发现 Nginx 很适合做代理,可以把一些奇奇怪怪的 8000、8080 等等端口的服务转发到 80/443 端口上,这样的话,我可以尽管用 Node.js 或 Flask 搭建 API,再使用 Nginx 转发到 80/443 即可。这样还有一个好处:我只需要在 Nginx 里面配置 Https 加密,而不用在 Node.js 或 Flask 做,这样上面所说的问题就解决了。
代理配置示例被我放到了 Gist 上,如下。
我把查分 API 的地址选在了 https://yusanshi.com/api/get_grade ,查阅 Nginx 文档,关于 location 有如下表述:
using the “
=
” modifier it is possible to define an exact match of URI and location. If an exact match is found, the search terminates
因此我选用 =
这个 modifier 来精确匹配 API 地址,最终的 Nginx 配置文件如下。
server {
server_name yusanshi.com;
......
location = /api/get_grade {
proxy_pass http://127.0.0.1:8082;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
它表示,如果请求 https://yusanshi.com/api/get_grade ,那么就将其转发到 http://127.0.0.1:8082 ,对应地,run.py
也要将 /api/get_grade
绑定到输出成绩的函数,并监听本地 8082 端口。
from flask import Flask, request, jsonify
from get_grade import get_grade
app = Flask(__name__)
@app.route('/api/get_grade', methods=['GET', 'POST'])
def return_grade():
if request.method == 'GET':
return "Use POST method please."
elif request.method == 'POST':
username = request.form['username']
password = request.form['password']
output = get_grade(username, password)
return jsonify(output)
if __name__ == '__main__':
app.run(port=8082)
上传完所有的文件,用 service nginx restart
重启 Nginx,测试一下。
看起来还好,基本上算是完工了。
呃,但是有红色的 WARNING。
WARNING: This is a development server. Do not use it in a production deployment.
在文档里找到如下说明:
When running publicly rather than in development, you should not use the built-in development server (
flask run
). The development server is provided by Werkzeug for convenience, but is not designed to be particularly efficient, stable, or secure.Instead, use a production WSGI server.
首先安装 waitress。
pip3 install waitress
再修改一下 run.py
。
......
import waitress
......
if __name__ == '__main__':
waitress.serve(app, host="127.0.0.1", port=8082)
这下没 WARNING 了。
更新:查成绩 API 地址改成了 https://yusanshi.com/api/get_grade/。(2020.3.18)
Comments