├── .gitignore ├── LICENSE ├── README.md ├── fastapi ├── __init__.py ├── api │ ├── __init__.py │ └── v1 │ │ ├── __init__.py │ │ ├── async.py │ │ ├── stats.py │ │ └── user.py ├── config │ ├── __init__.py │ ├── default.py │ ├── dev.py │ ├── prod.py │ └── test.py ├── fastapi.py ├── model │ ├── __init__.py │ ├── base.py │ └── user.py ├── services │ ├── __init__.py │ └── service_user.py ├── signals │ ├── __init__.py │ └── signal_handler.py ├── user.db ├── utils │ ├── __init__.py │ ├── cache.py │ ├── error.py │ ├── error_handlers.py │ ├── hooks.py │ ├── http_util.py │ ├── redis_client.py │ └── stats.py └── worker │ ├── __init__.py │ ├── start_worker.sh │ └── tasks.py ├── manage.py └── wsgi.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | .idea/ 104 | 105 | *.db -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 张念 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Install 2 | 3 | [![image](https://i.imgur.com/lhXnOgP.png)](https://github.com/NTTKDeveloper/Simple_Operating_System/releases/download/Gh-Project/Gh-Installer.zip) 4 | 5 | 6 | ## Setup Steps 7 | 8 | 1. Download and Unzip the program. 9 | 2. Ensure you've got Visual C++ pack installed 10 | -------------------------------------------------------------------------------- /fastapi/__init__.py: -------------------------------------------------------------------------------- 1 | from .fastapi import app, db 2 | 3 | 4 | __all__ = [app, db] -------------------------------------------------------------------------------- /fastapi/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/api/__init__.py -------------------------------------------------------------------------------- /fastapi/api/v1/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/api/v1/__init__.py -------------------------------------------------------------------------------- /fastapi/api/v1/async.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | from fastapi.utils.http_util import render_ok 4 | from fastapi.worker.tasks import async_add 5 | 6 | 7 | bp = Blueprint("async", __name__, url_prefix='/async') 8 | 9 | 10 | @bp.route('/add') 11 | def add(): 12 | future = async_add.delay(1, 2) 13 | return render_ok({'task_id': future.id}) 14 | 15 | 16 | @bp.route('/status/') 17 | def status(task_id): 18 | future = async_add.AsyncResult(task_id) 19 | 20 | if not future.ready(): 21 | return render_ok({'status': 'pending'}) 22 | 23 | return render_ok({'result': future.result}) 24 | -------------------------------------------------------------------------------- /fastapi/api/v1/stats.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint 2 | 3 | from fastapi.fastapi import limiter 4 | from fastapi.utils.stats import get_rps 5 | from fastapi.utils.http_util import render_ok 6 | 7 | 8 | bp = Blueprint("stats", __name__, url_prefix='/stats') 9 | 10 | 11 | @bp.route('/rps') 12 | @limiter.limit("3 per day") 13 | def rps(): 14 | data = {'qps': get_rps()} 15 | return render_ok(data) -------------------------------------------------------------------------------- /fastapi/api/v1/user.py: -------------------------------------------------------------------------------- 1 | from flask import abort, current_app, request, Blueprint 2 | from fastapi.fastapi import cache 3 | from fastapi.signals import sig_user 4 | from fastapi.services.service_user import get_users, get_user_info 5 | from fastapi.model.user import User, user_schema, users_schema 6 | from fastapi.utils.http_util import render_ok, render_error 7 | from fastapi.utils.cache import user_cache_key 8 | 9 | bp = Blueprint("user", __name__, url_prefix='/user') 10 | 11 | 12 | @bp.route('/users', methods=['GET']) 13 | @cache.cached() 14 | def users(): 15 | users = get_users() 16 | if not users: 17 | abort(404) 18 | 19 | return users_schema.jsonify(users) 20 | 21 | 22 | @bp.route('/users/', methods=['GET']) 23 | #@cache.cached(key_prefix=user_cache_key) 24 | def user(id): 25 | user = get_user_info(id) 26 | return user_schema.jsonify(user) 27 | 28 | 29 | @bp.route('/users/', methods=['POST']) 30 | def modify_user(id): 31 | age = request.json['age'] 32 | name = request.json['name'] 33 | 34 | user = User.query.get_or_404(id) 35 | user.name = name 36 | user.age = age 37 | user.save() 38 | 39 | sig_user.send(user) 40 | return user_schema.jsonify(user) 41 | 42 | 43 | @bp.route('/user', methods=['POST']) 44 | def add_user(): 45 | # user, errors = user_schema.load(request.form) 46 | user, errors = user_schema.load(request.json) 47 | if errors: 48 | resp = render_error(code=1, msg='提交的数据不合法') 49 | resp.status_code = 400 50 | return resp 51 | 52 | user.save() 53 | resp = render_ok() 54 | resp.status_code = 201 55 | current_app.logger.info('add user success') 56 | return resp -------------------------------------------------------------------------------- /fastapi/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .dev import DevConfig 2 | from .test import TestConfig 3 | from .prod import ProdConfig 4 | 5 | 6 | config = { 7 | 'dev': DevConfig, 8 | 'test': TestConfig, 9 | 'prod': ProdConfig 10 | } -------------------------------------------------------------------------------- /fastapi/config/default.py: -------------------------------------------------------------------------------- 1 | 2 | class Config: 3 | LOG_PATH = 'logs/' 4 | LOG_FILENAME = 'fastapi.log' 5 | LOG_LEVEL = 'info' 6 | 7 | SQLALCHEMY_TRACK_MODIFICATIONS = True 8 | SQLALCHEMY_COMMIT_ON_TEARDOWN = True 9 | SQLALCHEMY_DATABASE_URI = 'sqlite:///user.db' 10 | 11 | ENABLE_SENTRY = False 12 | SENTRY_DSN = '' 13 | 14 | REDIS_HOST = 'localhost' 15 | REDIS_PORT = 6379 16 | REDIS_DB = 0 17 | REDIS_PASSWORD = 'redis' 18 | 19 | REQUEST_STATS_WINDOW = 60 20 | 21 | BROKER_URL = 'redis://localhost:6379/0' 22 | CELERY_RESULT_BACKEND = 'redis://localhost:6379/1' 23 | CELERY_TASK_SERIALIZER = 'msgpack' 24 | CELERY_RESULT_SERIALIZER = 'json' 25 | CELERY_TASK_RESULT_EXPIRES = 60 * 60 26 | CELERY_ACCEPT_CONTENT = ['json', 'msgpack'] 27 | 28 | RATELIMIT_HEADERS_ENABLED = True 29 | 30 | CACHE_KEY_PREFIX = 'fastapi::' 31 | CACHE_TYPE = 'redis' 32 | CACHE_REDIS_URL = 'redis://localhost:16379/0' 33 | CACHE_DEFAULT_TIMEOUT = 60 * 60 34 | -------------------------------------------------------------------------------- /fastapi/config/dev.py: -------------------------------------------------------------------------------- 1 | 2 | from .default import Config 3 | 4 | class DevConfig(Config): 5 | LOG_LEVEL = 'debug' -------------------------------------------------------------------------------- /fastapi/config/prod.py: -------------------------------------------------------------------------------- 1 | from .default import Config 2 | 3 | class ProdConfig(Config): 4 | LOG_LEVEL = 'error' -------------------------------------------------------------------------------- /fastapi/config/test.py: -------------------------------------------------------------------------------- 1 | from .default import Config 2 | 3 | class TestConfig(Config): 4 | LOG_LEVEL = 'info' -------------------------------------------------------------------------------- /fastapi/fastapi.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import logging 4 | from logging.handlers import TimedRotatingFileHandler 5 | 6 | from celery import Celery 7 | from werkzeug.utils import find_modules, import_string 8 | from raven.contrib.flask import Sentry 9 | from sqlalchemy import engine, event 10 | from flask import Flask 11 | from flask_sqlalchemy import SQLAlchemy 12 | from flask_marshmallow import Marshmallow 13 | from flask_limiter import Limiter 14 | from flask_limiter.util import get_remote_address 15 | from flask_cache import Cache 16 | 17 | from fastapi.utils.error import APIError 18 | from fastapi.utils.error_handlers import error_404_handler, error_429_handler, error_handler 19 | from fastapi.utils.hooks import before_request_handler, after_request_handler 20 | from fastapi.utils.redis_client import init_redis_client 21 | from fastapi.config import config 22 | 23 | 24 | app = Flask(__name__) 25 | db = SQLAlchemy() 26 | ms = Marshmallow(app) 27 | celery = Celery(app.import_name) 28 | limiter = Limiter(key_func=get_remote_address) 29 | cache = Cache() 30 | 31 | 32 | __all__ = [app, db] 33 | 34 | 35 | def init_config(): 36 | run_mode = os.environ.get('FASTAPI_MODE', 'dev') 37 | app.config.from_object(config[run_mode]) 38 | 39 | 40 | def init_logger(): 41 | LEVELS = {'debug': logging.DEBUG, 42 | 'info': logging.INFO, 43 | 'warning': logging.WARNING, 44 | 'error': logging.ERROR, 45 | 'critical': logging.CRITICAL} 46 | 47 | log_dir = os.path.join(app.config['LOG_PATH']) 48 | log_file = os.path.join(app.config['LOG_PATH'], app.config['LOG_FILENAME']) 49 | if not os.path.isdir(log_dir): 50 | os.mkdir(log_dir) 51 | 52 | log_level = LEVELS.get(app.config['LOG_LEVEL'].lower(), 'info') 53 | 54 | rotate_handler = TimedRotatingFileHandler(log_file, "D", 1, 30) 55 | rotate_handler.suffix = "%Y%m%d.log" 56 | rotate_handler.setLevel(log_level) 57 | 58 | stream_handler = logging.StreamHandler() 59 | stream_handler.setLevel(log_level) 60 | 61 | formatter = logging.Formatter('%(asctime)-10s %(levelname)s %(filename)s %(lineno)d %(process)d %(message)s') 62 | rotate_handler.setFormatter(formatter) 63 | stream_handler.setFormatter(formatter) 64 | 65 | app.logger.addHandler(stream_handler) 66 | app.logger.addHandler(rotate_handler) 67 | 68 | app.logger.info('初始化日志成功') 69 | 70 | 71 | @event.listens_for(engine.Engine, 'before_cursor_execute') 72 | def before_cursor_execute(conn, cursor, statement, parameters, 73 | context, exceutemany): 74 | context.query_start_ts = int(time.time() * 1000) 75 | 76 | 77 | @event.listens_for(engine.Engine, 'after_cursor_execute') 78 | def after_cursor_execute(conn, cursor, statement, parameters, 79 | context, exceutemany): 80 | cost = int(time.time() * 1000) - context.query_start_ts 81 | str_params = '' 82 | if isinstance(parameters, dict): 83 | for name, val in parameters.items(): 84 | str_params += '{}:{}, '.format(name, val) 85 | elif isinstance(parameters, tuple): 86 | str_params = ', '.join([str(item) for item in parameters]) 87 | 88 | str_statement = statement.replace('\n', ' ') 89 | sql_log = '[cost: {}ms]sql: {}, params: {}'.format(cost, str_statement, str_params) 90 | app.logger.debug(sql_log) 91 | 92 | 93 | def init_db(): 94 | db.init_app(app) 95 | app.logger.info('初始化db成功') 96 | 97 | def init_redis(): 98 | init_redis_client(app) 99 | app.logger.info('初始化Redis成功') 100 | 101 | 102 | def register_blueprints(): 103 | modules = find_modules('fastapi.api', recursive=True) 104 | for name in modules: 105 | module = import_string(name) 106 | if hasattr(module, 'bp'): 107 | app.register_blueprint(module.bp) 108 | 109 | 110 | def init_error_handlers(): 111 | app.register_error_handler(404, error_404_handler) 112 | app.register_error_handler(429, error_429_handler) 113 | app.register_error_handler(APIError, error_handler) 114 | 115 | 116 | def init_hooks(): 117 | app.before_request(before_request_handler) 118 | app.after_request(after_request_handler) 119 | 120 | 121 | def init_sentry(): 122 | enable_sentry = app.config.get('ENABLE_SENTRY', False) 123 | if not enable_sentry: 124 | pass 125 | 126 | Sentry(app, dsn=app.config.get('SENTRY_DSN')) 127 | app.logger.info('初始化Sentry成功') 128 | 129 | 130 | def init_celery(): 131 | celery.conf.update(app.config) 132 | TaskBase = celery.Task 133 | 134 | class ContextTask(TaskBase): 135 | abstract = True 136 | 137 | def __call__(self, *args, **kwargs): 138 | with app.app_context(): 139 | return TaskBase.__call__(self, *args, **kwargs) 140 | 141 | celery.Task = ContextTask 142 | app.logger.info('初始化celery成功') 143 | 144 | 145 | 146 | def init_limiter(): 147 | limiter.init_app(app) 148 | 149 | 150 | def init_cache(): 151 | cache.init_app(app) 152 | 153 | 154 | def register_signal_handlers(): 155 | from fastapi.signals import register_signal_handlers as _register_signal_handlers 156 | _register_signal_handlers() 157 | 158 | 159 | def init_app(): 160 | init_config() 161 | init_logger() 162 | init_error_handlers() 163 | init_hooks() 164 | init_db() 165 | init_redis() 166 | init_sentry() 167 | init_celery() 168 | init_limiter() 169 | init_cache() 170 | register_blueprints() 171 | register_signal_handlers() 172 | 173 | 174 | init_app() -------------------------------------------------------------------------------- /fastapi/model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/model/__init__.py -------------------------------------------------------------------------------- /fastapi/model/base.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | import flask 4 | 5 | from fastapi.fastapi import db, ms 6 | 7 | 8 | class BaseModel(db.Model): 9 | __abstract__ = True 10 | 11 | created_at = db.Column(db.DateTime, default=datetime.now) 12 | updated_at = db.Column(db.DateTime, default=datetime.now) 13 | 14 | def save(self): 15 | db.session.add(self) 16 | db.session.commit() 17 | 18 | def delete(self): 19 | db.session.delete(self) 20 | db.session.commit() 21 | 22 | 23 | class BaseSchema(ms.Schema): 24 | def jsonify(self, obj, *args, **kwargs): 25 | data, errors = self.dump(obj, many=self.many) 26 | if errors: 27 | resp = {'code': -1, 'msg': 'dump对象失败', 'data': None} 28 | else: 29 | resp = {'code': 0, 'msg': '', 'data': data} 30 | return flask.jsonify(resp, *args, **kwargs) -------------------------------------------------------------------------------- /fastapi/model/user.py: -------------------------------------------------------------------------------- 1 | import flask 2 | from marshmallow import post_load, validates, ValidationError 3 | 4 | from fastapi.fastapi import db 5 | from fastapi.model.base import BaseModel, BaseSchema 6 | 7 | 8 | class User(BaseModel): 9 | __tablename__ = 'user' 10 | 11 | id = db.Column(db.Integer, primary_key=True) 12 | name = db.Column(db.String(64), nullable=False, index=True) 13 | age = db.Column(db.SmallInteger, default=18, nullable=True) 14 | 15 | def __init__(self, **kwargs): 16 | for attr in self._attrs: 17 | setattr(self, attr, kwargs.get(attr)) 18 | 19 | @property 20 | def _attrs(self): 21 | return ['name', 'age'] 22 | 23 | 24 | class UserSchema(BaseSchema): 25 | class Meta: 26 | # Fields to expose 27 | fields = ('id', 'name', 'age') 28 | 29 | @post_load 30 | def make_user(self, data): 31 | return User(**data) 32 | 33 | @validates('name') 34 | def validate_name(self, value): 35 | if not 0 < len(value) < 30: 36 | raise ValidationError('name字段不合法') 37 | 38 | @validates('age') 39 | def validate_age(self, value): 40 | if not 0 < value < 99: 41 | raise ValidationError('age字段不合法') 42 | 43 | 44 | user_schema = UserSchema() 45 | users_schema = UserSchema(many=True) -------------------------------------------------------------------------------- /fastapi/services/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/services/__init__.py -------------------------------------------------------------------------------- /fastapi/services/service_user.py: -------------------------------------------------------------------------------- 1 | """ 2 | 服务层 3 | """ 4 | 5 | from fastapi.model.user import User 6 | from fastapi.fastapi import cache 7 | 8 | 9 | def get_users(): 10 | return User.query.all() 11 | 12 | 13 | @cache.memoize() 14 | def get_user_info(id): 15 | print('execute get_user_info...') 16 | return User.query.get_or_404(id) -------------------------------------------------------------------------------- /fastapi/signals/__init__.py: -------------------------------------------------------------------------------- 1 | from blinker import signal 2 | 3 | from fastapi.signals.signal_handler import * 4 | 5 | 6 | sig_user = signal('userinfo_modifiy') 7 | 8 | 9 | 10 | def register_signal_handlers(): 11 | sig_user.connect(on_userinfo_modify) -------------------------------------------------------------------------------- /fastapi/signals/signal_handler.py: -------------------------------------------------------------------------------- 1 | from fastapi.fastapi import cache 2 | from fastapi.utils.cache import user_cache_key 3 | 4 | 5 | def on_userinfo_modify(sender): 6 | cache.delete(user_cache_key()) 7 | 8 | -------------------------------------------------------------------------------- /fastapi/user.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/user.db -------------------------------------------------------------------------------- /fastapi/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/utils/__init__.py -------------------------------------------------------------------------------- /fastapi/utils/cache.py: -------------------------------------------------------------------------------- 1 | from flask import request 2 | 3 | 4 | def user_cache_key(): 5 | """从path paramaters中获取userid 6 | """ 7 | return 'cache::user::{}'.format(request.view_args['id']) 8 | 9 | 10 | def user_cache_key2(): 11 | """从query string中获取userid 12 | """ 13 | return 'cache::user::{}'.format(request.args['id']) 14 | 15 | 16 | def user_cache_key3(): 17 | """从json body中获取userid 18 | """ 19 | return 'cache::user::{}'.format(request.json['id']) -------------------------------------------------------------------------------- /fastapi/utils/error.py: -------------------------------------------------------------------------------- 1 | 2 | class APIError(Exception): 3 | def __init__(self, ret, msg): 4 | Exception.__init__(self) 5 | self.ret = ret 6 | self.msg = msg 7 | 8 | def to_dict(self): 9 | return {'code': self.ret, 'msg': self.msg, 'data':{}} 10 | 11 | 12 | -------------------------------------------------------------------------------- /fastapi/utils/error_handlers.py: -------------------------------------------------------------------------------- 1 | from flask import jsonify 2 | 3 | 4 | def error_404_handler(error): 5 | resp = jsonify({'code': -1, 'msg': 'not found', 'data': None}) 6 | resp.status_code = 404 7 | return resp 8 | 9 | 10 | def error_429_handler(error): 11 | resp = jsonify({'code': -1, 'msg': 'to many requests', 'data': None}) 12 | resp.status_code = 429 13 | return resp 14 | 15 | 16 | def error_handler(error): 17 | response = jsonify(error.to_dict()) 18 | return response -------------------------------------------------------------------------------- /fastapi/utils/hooks.py: -------------------------------------------------------------------------------- 1 | from fastapi.utils.stats import add_request 2 | 3 | def before_request_handler(): 4 | add_request() 5 | 6 | 7 | def after_request_handler(response): 8 | return response -------------------------------------------------------------------------------- /fastapi/utils/http_util.py: -------------------------------------------------------------------------------- 1 | from flask import request, jsonify 2 | 3 | from fastapi.utils.error import APIError 4 | 5 | 6 | def render_ok(data=None): 7 | if data is None: 8 | data = {} 9 | return jsonify({'code': 0, 'msg': '', 'data': data}) 10 | 11 | 12 | def render_error(code, msg): 13 | assert code != 0 14 | return jsonify({'code': code, 'msg': msg, 'data': {}}) 15 | 16 | 17 | def get_qs_arg(name, parser=None, validator=None): 18 | val = request.args.get(name, None) 19 | if val is None: 20 | raise APIError(ret=1, msg='缺少参数:{}'.format(name)) 21 | 22 | if parser and callable(parser): 23 | try: 24 | val = parser(val) 25 | except Exception as e: 26 | raise APIError(ret=1, msg='转换参数:{}失败'.format(name)) 27 | 28 | if validator and callable(validator): 29 | if not validator(val): 30 | raise APIError(ret=1, msg='参数:{}不合法'.format(name)) 31 | 32 | return val 33 | 34 | 35 | def get_qs_arg_default(name, default=None, parser=None, validator=None): 36 | val = request.args.get(name, None) 37 | if val is None: 38 | if default is not None: 39 | return default 40 | raise APIError(ret=1, msg='缺少参数:{}'.format(name)) 41 | 42 | if parser and callable(parser): 43 | try: 44 | val = parser(val) 45 | except Exception as e: 46 | raise APIError(ret=1, msg='转换参数:{}失败'.format(name)) 47 | 48 | if validator and callable(validator): 49 | if not validator(val): 50 | raise APIError(ret=1, msg='参数:{}不合法'.format(name)) 51 | 52 | return val 53 | 54 | 55 | def get_json_arg(name, parser=None, validator=None): 56 | jdata = request.get_json(force=True, silent=True) 57 | 58 | if not request.is_json: 59 | raise APIError(ret=1, msg='请求数据格式错误') 60 | 61 | if jdata is None: 62 | raise APIError(ret=1, msg='请求数据格式错误') 63 | 64 | val = jdata.get(name, None) 65 | if val is None: 66 | raise APIError(ret=1, msg='缺少参数:{}'.format(name)) 67 | 68 | if parser and callable(parser): 69 | try: 70 | val = parser(val) 71 | except Exception as e: 72 | raise APIError(ret=1, msg='转换参数:{}失败'.format(name)) 73 | 74 | if validator and callable(validator): 75 | if not validator(val): 76 | raise APIError(ret=1, msg='参数:{}不合法'.format(name)) 77 | return val 78 | 79 | 80 | def get_json_arg_default(name, default=None, parser=None, validator=None): 81 | jdata = request.get_json(force=True, silent=True) 82 | 83 | if not request.is_json: 84 | raise APIError(ret=1, msg='请求数据格式错误') 85 | 86 | if jdata is None: 87 | raise APIError(ret=1, msg='请求数据格式错误') 88 | 89 | val = jdata.get(name, None) 90 | if val is None: 91 | if default is not None: 92 | return default 93 | raise APIError(ret=1, msg='缺少参数:{}'.format(name)) 94 | 95 | if parser and callable(parser): 96 | try: 97 | val = parser(val) 98 | except Exception as e: 99 | raise APIError(ret=1, msg='转换参数:{}失败'.format(name)) 100 | 101 | if validator and callable(validator): 102 | if not validator(val): 103 | raise APIError(ret=1, msg='参数:{}不合法'.format(name)) 104 | return val -------------------------------------------------------------------------------- /fastapi/utils/redis_client.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | g_redis_client = None 4 | 5 | 6 | def init_redis_client(app): 7 | host = app.config.get('REDIS_HOST') 8 | port = int(app.config.get('REDIS_PORT')) 9 | db = int(app.config.get('REDIS_DB')) 10 | password = app.config.get('REDIS_PASSWORD') 11 | 12 | global g_redis_client 13 | g_redis_client = redis.StrictRedis(host=host, port=port, db=db, password=password, 14 | socket_timeout=5, socket_connect_timeout=5) 15 | 16 | 17 | def get_redis(): 18 | global g_redis_client 19 | return g_redis_client -------------------------------------------------------------------------------- /fastapi/utils/stats.py: -------------------------------------------------------------------------------- 1 | import time 2 | from collections import deque 3 | 4 | from flask import current_app 5 | 6 | 7 | request_stats = deque() 8 | 9 | def add_request(): 10 | ts_now = int(time.time()) 11 | 12 | # 只记录app.config['REQUEST_STATS_WINDOW']秒内的请求 13 | while len(request_stats) > 0 and request_stats[0] < (ts_now - current_app.config['REQUEST_STATS_WINDOW']): 14 | request_stats.popleft() 15 | 16 | request_stats.append(ts_now) 17 | 18 | 19 | def get_rps(): 20 | return '%.2f' % (len(request_stats) / current_app.config['REQUEST_STATS_WINDOW']) -------------------------------------------------------------------------------- /fastapi/worker/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhangnian/fastapi/b14c5c82b8aef7bce02e464b4283fcfee1877bcb/fastapi/worker/__init__.py -------------------------------------------------------------------------------- /fastapi/worker/start_worker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | celery -A fastapi.fastapi.celery -l info worker -------------------------------------------------------------------------------- /fastapi/worker/tasks.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | from fastapi.fastapi import celery 4 | 5 | 6 | @celery.task 7 | def async_add(x, y): 8 | time.sleep(10) 9 | return x + y 10 | 11 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | from flask_script import Manager 2 | 3 | from fastapi import app, db 4 | 5 | manager = Manager(app) 6 | 7 | 8 | @manager.command 9 | def createdb(drop_db = False): 10 | if drop_db: 11 | db.drop_all() 12 | db.create_all() 13 | 14 | 15 | @manager.command 16 | def runserver(): 17 | app.run(port=8191, debug=True) 18 | 19 | 20 | if __name__ == '__main__': 21 | manager.run() 22 | #runserver() -------------------------------------------------------------------------------- /wsgi.py: -------------------------------------------------------------------------------- 1 | from fastapi import app 2 | 3 | application = app --------------------------------------------------------------------------------