使用Flask-Restful實現API接口

1. 代碼結構:

2. 具體代碼

2.1 user.py

from ApiRESTful.extensions import db
from werkzeug.security import generate_password_hash, check_password_hash


class User(db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), unique=True, index=True)
    # 將password設置為私有屬性,并且重命名
    _password = db.Column('password', db.String(128))

    # 定義一個屬性,默認是讀取的操作,這里報錯,意思是不可讀
    @property
    def password(self):
        raise AttributeError('password is not readable attribute')

    # 定義上面那個password屬性的可寫屬性,這里默認換算成哈希值,然后保存下來
    @password.setter
    def password(self, password):
        self._password = generate_password_hash(password)

    # 校驗傳入的密碼和哈希值是否是一對兒
    def verify_password(self, password):
        return check_password_hash(self._password, password)

    def __repr__(self):
        return "<User {}>".format(self.username)

2.2 api_auth.py

Flask-Httpauth是用來驗證用戶的,但在這個部分中沒有用到,之后會專門來寫一個。

from flask_httpauth import HTTPBasicAuth
from flask import jsonify, app
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from itsdangerous import SignatureExpired, BadSignature

from ApiRESTful.settings import DevelopConfig
from ApiRESTful.models.user import User

auth = HTTPBasicAuth()


@(FLASK)auth.error_handler
def unauthorized():
    error_info = '{}'.format('Invalid credentials')
    print('api.auth.unauthorized.error_info = ' + error_info)
    response = jsonify({'error': error_info})
    response.status_code = 403

    print('api.auth.unauthorized.response = ' + str(response))

    return response


def verify_password_for_token(username, password):
    """
    驗證輸入的用戶名和密碼是否匹配
    :param username: 用戶名
    :param password: 密碼
    :return: 匹配結果
    """
    user = User.query.filter_by(username=username).first()
    # 注意一下,以后判斷是否為空,還是寫成 user is None 而不要寫成 not user
    # 這樣寫更容易理解一些
    if user is None or not user.verify_password(password):
        # 結果不匹配
        return False

    return True


@auth.verify_password
def verify_password(username_or_token, password):
    user = verify_auth_token(username_or_token)
    if user is None:
        return verify_password_for_token(username_or_token, password)

    return user


def generator_auth_token(expiration=600):
    s = Serializer(secret_key=DevelopConfig.SECRET_KEY, expires_in=expiration)

    return s.dumps({'id': 1})


def verify_auth_token(token):
    s = Serializer(DevelopConfig.SECRET_KEY)

    try:
        data = s.loads(token)
    except SignatureExpired:
        return None
    except BadSignature:
        return None

    user = User.query.get(data.get('id'))

    return user

2.3 errors.py

使用Blurprint的errorhandler來定義錯誤處理函數。

from flask import jsonify

from ApiRESTful.blueprints.api import api_bp


@api_bp.errorhandler(404)
def not_found(e):
    print('api.errors.not_found ', e)
    error_info = '{}'.format(e)
    response = jsonify({'error': error_info})
    response.status_code = 404

    return response


@api_bp.errorhandler(403)
def fobidden(e):
    print(e)
    error_info = '{}'.format(e)
    response = jsonify({'error': error_info})
    response.status_code = 403
    return response

2.4 api/__init__.py

init.py中創建Blueprint對象api_bp

from flask import Blueprint

api_bp = Blueprint('api', __name__, url_prefix='/api')

from . import api_auth, api_user

2.5 api.user.py

  1. 這部分是主要的代碼,從flask_restful庫中導入Api和Resouce類,并且創建Api對象,創建對象時需要傳入Blueprint對象,即api_bp。
  2. 實現一個接口時,需要創建一個類,并且該類要繼承Resouce。
  3. 在繼承了Resouce的類中,post表示接收的是POST請求,get接收的是get請求....其他一樣。
  4. 使用api_user.add_resource函數去申明接口的訪問url,api_user是Api對象,例如
    api_user.add_resource(UserAddApi, '/useradd', endpoint='useradd')
    第一個參數是Resouce類,即上面定義的繼承了Resouce的子類,在IDE顯示警告,不用在意;第二個參數就是path了;第三個參數的endpoint是在代碼內部訪問的,跟Flask中一樣,在一般情況下是不要寫的,即訪問方式url_for('api_user.useradd')
  5. (4)中的訪問完整鏈接是http://127.0.0.1:5000/api/useradd
  6. 在定義接口時,返回的數據類型都應該是json格式
  7. 網頁訪問時可能會post數據,數據類型是json格式的話,需要將它進行轉換
    json.loads(request.get_json())
import time
import json

from ApiRESTful.extensions import db
from flask_restful import Api, Resource
from flask import jsonify, request
from . import api_bp
from ApiRESTful.models.user import User
from ApiRESTful.blueprints.api.api_auth import auth, generator_auth_token, verify_auth_token

api_user = Api(api_bp)


class UserAddApi(Resource):
    def post(self):
        print('UserAddApi.post.url = ' + str(request.url))
        # 如果傳入進來的是json數據,那么需要轉換成dict類型的數據
        user_info = json.loads(request.get_json())
        print('user_info.type =', type(user_info))
        print('UserAddApi.post.user_info = ' + str(user_info))
        try:
            u = User(username=user_info['username'])
            u.password = user_info['password']
            db.session.add(u)
            db.session.commit()
        except:
            print("{} User add: {} failure...".format(time.strftime("%Y-%m-%d %H:%M:%S"), user_info['username']))
            db.session.rollback()
            return False
        else:
            print("{} User add: {} success...".format(time.strftime("%Y-%m-%d %H:%M:%S"), user_info['username']))
            return True
        finally:
            db.session.close()


class UserVerifyApi(Resource):
    def post(self):
        print('UserVerifyApi.post.url = ' + str(request.url))
        user_info = json.loads(request.get_json())
        try:
            u = User.query.filter_by(username=user_info['username']).first()
            if u is None or u.verify_password(user_info['password']) is False:
                print("{} User query: {} failure...".format(time.strftime("%Y-%m-%d %H:%M:%S"), user_info['username']))
                return False
        except:
            print("{} User query: {} failure...".format(time.strftime("%Y-%m-%d %H:%M:%S"), user_info['username']))
            return False
        else:
            print("{} User query: {} success...".format(time.strftime("%Y-%m-%d %H:%M:%S"), user_info['username']))
            return True
        finally:
            db.session.close()


class UserTokenApi(Resource):
    # @auth.login_required
    def get(self):
        print('UserTokenApi.get.url = ' + str(request.url))
        token = generator_auth_token(expiration=600)
        return jsonify({'token': token.decode('ascii')})


api_user.add_resource(UserAddApi, '/useradd', endpoint='useradd')
api_user.add_resource(UserVerifyApi, '/userverify', endpoint='userverify')
api_user.add_resource(UserTokenApi, '/usertoken', endpoint='usertoken')

2.6 extensions.py

該類都會用來放第三方插件的對象

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

2.7 settings.py

配置類,會根據不同的場景配置不同的屬性,這個只是demo,所以就不那么嚴格了。

import os

basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))


class DevelopConfig(object):
    SECRET_KEY = 'a random string'

    SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:root@localhost:3306/api'
    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False

    DEBUG = True

2.8 ApiRESTful/__init__.py

創建Flask對象,以及做一些初始化操作,例如導入第三方插件的對象,然后調用init_app(app)等操作。

from flask import Flask

from ApiRESTful.settings import DevelopConfig
from ApiRESTful.extensions import db
from ApiRESTful.blueprints.api import api_bp


def register_blueprints(_app):
    _app.register_blueprint(api_bp)


def register_extensions(_app):
    db.init_app(_app)
    init_db(_app)


def init_db(_app: Flask):
    _app.app_context().push()
    db.drop_all()
    db.create_all()
    db.session.commit()


def _create_app():
    _app = Flask(__name__)
    _app.config.from_object(DevelopConfig)

    register_blueprints(_app)
    register_extensions(_app)

    return _app


app = _create_app()


2.9 manage.py

程序入口,執行manage.py就可以啟動web應用了。

from flask_script import Manager, Server, Shell

from ApiRESTful import app
from ApiRESTful.extensions import db

from ApiRESTful.models.user import User

manager = Manager(app)


def make_shell_context():
    return dict(app=app, db=db, User=User)


manager.add_command('runserver', Server(host='127.0.0.1', port=5000, use_debugger=True, use_reloader=True))
manager.add_command('shell', Shell(make_context=make_shell_context))

if __name__ == '__main__':
    manager.run(default_command='runserver')

3.測試

測試代碼

import requests

resp = requests.post(url='http://127.0.0.1:5000/api/useradd',
                     json="{\"username\": \"chentao\", \"password\": \"123456\"}")
print(resp)

r2 = requests.get(url='http://127.0.0.1:5000/api/usertoken')
print(r2)
print(r2.json())

r3 = requests.post(url='http://127.0.0.1:5000/api/userverify',
                   json="{\"username\": \"chentao\", \"password\": \"123456\"}")
print(r3.json())
print(r3)

4.參考

http://www.lxweimin.com/p/81cd461c7e8f
https://flask-httpauth.readthedocs.io/en/latest/
http://www.pythondoc.com/Flask-RESTful/quickstart.html
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容