Flask官方文档:https://dormousehole.readthedocs.io/en/latest/index.html
本flask接口实现功能
1)入参无‘json’时,返回csv文件
2)入参json有任意值(如:json=1)时,在前端返回json
3)各入参均可接收多个(以逗号分隔,如 http://0.0.0.0:8888/myapi?studentId=101,102&classId=2,5 将查询classId为2或5、且studentId为101或102的学生信息)
4)入参中只需保证一个不为空
#grade_api.py
# -- coding:UTF-8 --
import json
import time
import os
from flask import Flask, request, send_from_directory, make_response, jsonify
import logging
import configparser
from datetime import timedelta, datetime
import grade_record
app = Flask(__name__) #创建flask对象
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=2)
# flask对象的config属性;SEND_FILE_MAX_AGE_DEFAULT:默认缓存控制的最大期限,以秒计.
# 更多属性 http://docs.jinkan.org/docs/flask/config.html
app.config['JSON_AS_ASCII'] = False
TEMPLATES_AUTO_RELOAD = True
# 使用 route() 装饰器来告诉 Flask 触发函数的 URL 。
@app.route('/myapi', methods=['get', 'post'])
def find_grade():
student_id = request.values.get('studentId') # 获取请求对象
student_id = get_list(student_id) #获取studentId列表
class_id = request.values.get('classId')
class_id = get_list(class_id)
json = request.values.get('json')
if student_id or class_id :
#只要一个入参不为空
grade_record.get_record(student_id, class_id) # 从sql库查询记录(且将记录转为csv)
directory = os.getcwd()
#若json参数不为空,则返回json,否则返回csv文件
if json:
response = make_response(jsonify(grade_record.get_df()))
else:
response = make_response(send_from_directory(directory, grade_record.get_file_name(), as_attachment=True))
grade_record.remove_oudate_files(2) # 删除当前目录下2min前的csv文件
return response
else:
resu = {'code': 10001, 'message': '参数不能全为空!'}
return json.dumps(resu, ensure_ascii=False)
def get_list(field_name):
if field_name:
return process_list(field_name)
def process_list(field_name):
#处理接收到的参数,转为list
if field_name:
field_list = list(field_name.split(','))
return field_list
else:
print(field_name + 'is none')
if __name__ == '__main__':
app.run(debug=config['api']['debug'], port=config['api']['port'], host=config['api']['host'])
#grade_record.py
# -*- coding:utf-8 -*-
import time
import pymysql
import configparser
import os
import numpy as np
import pandas as pd
import pressure
from sshtunnel import SSHTunnelForwarder
import paramiko
config = configparser.RawConfigParser()
config.read('config.ini', encoding='UTF-8')
def sql_conn():
global conn_sql
# 为"True"时,连接sql测试服
if config['ssh']['ssh_connect'] == 'True':
global server
server = SSHTunnelForwarder(
(config['ssh']['server_host'], 22),
ssh_username=config['ssh']['ssh_username'],
ssh_pkey='/Users/.ssh/id_rsa', #私钥地址
remote_bind_address=(config['ssh']['remote_bind_address'], 3306)
)
server.start()
conn_sql = pymysql.connect(host=config['sql_test']['host'],
port=server.local_bind_port,
user=config['sql_test']['user'],
password=config['sql_test']['password'],
db=config['sql_test']['db'],
charset='utf8')
global cur
cur = conn_sql.cursor()
def get_sql_connection():
return conn_sql
def get_sql_cursor():
return cur
def sql_select(student_id, class_id):
# sql查询语句
sql_base = " SELECT class_id,student_id,grade FROM table_grade WHERE "
global params
params = [student_id, class_id]
# 遍历接口入参列表params,不为空的项拼接其对应的sql语句
count = 0
for i in range(0, len(columns)):
if params[i]:
if count == 0:
sql_base = sql_base + sql_joint(columns[i])
else:
sql_base = sql_base + ' AND ' + sql_joint(columns[i])
count = count + 1
else:
continue
return sql_base
def get_field_bunch(field_list_name):
# 列表转化为()形式的字符串,便于sql查询
field_bunch = '(%s)' % ','.join(map(str, field_list_name))
return field_bunch
def sql_joint(field_name):
if field_name == 'student_id':
return " student_id in " + get_field_bunch(params[0])
elif field_name == 'class_id':
return " class_id in " + get_field_bunch(params[1])
def get_record(student_id, class_id):
sql_conn()
cur.execute(sql_select(student_id, class_id))
result = cur.fetchall()
df = pd.DataFrame(list(result),
columns=['class_id', 'student_id', 'grade'])
cur_time = time.strftime('%Y%m%d_%H_%M_%S', time.localtime(time.time()))
global file_name
file_name = cur_time + '.csv'
df_csv = df.to_csv(file_name, encoding='utf_8_sig', index=False) # 取当前时间命名csv文件
# 编码utf_8_sig能解决csv在windows中打开乱码的问题
global df_dict
df_dict = df.T.to_dict()
conn_sql.commit()
conn_sql.close()
return df_csv
def get_df():
return df_dict
def get_file_name():
return file_name
def remove_oudate_files(minutes):
# 删除当前目录下历史csv文件
ts_before = time.time()
path = os.getcwd()
for file in os.listdir(path):
filename = path + os.sep + file
if filename.endswith('.csv'):
if os.path.getatime(filename) < ts_before:
try:
os.remove(filename)
print('%s remove success' % filename)
except Exception as error:
print(error)
print('%s remove failed' % filename)
bug解决
bug | 原因 | 解决 | |
结果中有中文字符时,flask返回的json里的中文为unicode编码(如\u5b66\u751f ... ) | 默认情况下 Flask 使用 ascii 编码来序列化对象。 | app.config['JSON_AS_ASCII'] = False # JSON_AS_ASCII属性:默认情况下 Flask 使用 ascii 编码来序列化对象。如果这个值被设置为 False , Flask不会将其编码为 ASCII,并且按原样输出,返回它的 unicode 字符串。 | |
若入参和上一次输入的相同,返回的csv文件仍是缓存中的上一个文件(文件名以生成时间命名的话,此时返回新文件名仍是上一次的时间) | app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(seconds=2) # 设置默认缓存控制的最大期限,以秒计。 | ||
dataframe转 json,并在response中 | flask的jsonify方法不能直接转dataframe; 但可以直接转字典 | 先将dataframe转为dict,再用 jsonify(dict) | jsonify()和json.dumps()的区别
jsonify不仅会将内容转换为json,而且也会修改Content-Type为application/json。
用json.dumps时,HTTP返回的Content-Type仍然是text/html 还需要手动设置 content_type:
|