如何使用 Flask-oauthlib 连接多个 API(例如 Twitter、GitHub 等)?目前,我将每一项服务作为单独的蓝图。在每个服务的视图文件中,有三个相同的视图:登录、授权和获取令牌。现在的代码并不是很 DRY,我难以理解如何集中处理这些视图(更多是概念上的)。
如何才能使代码更加 DRY?我希望更多地从概念上理解,而不是让我只写代码。
下面列出了一些可能有助于理解这个问题的信息。这是应用程序的结构:
- App
- Services
- FourSquare BP
- GitHub BP
- Twitter BP
- ...
- Other BPs
- Services
通用 API 视图可能会放在 Services/api_views.py 中
以下是 API 蓝图视图文件之一(Twitter)的示例。
twitter = Blueprint('twitter', __name__, url_prefix='/twitter')
bp = twitter
bp.api = TwitterAPI()
bp.oauth = bp.api.oauth_app
# 下面是每个文件中确切相同的内容。
@bp.route('/')
@login_required
def login():
if current_user.get(bp.name, None):
return redirect(url_for('frontend.index'))
return bp.oauth.authorize(callback=url_for('.authorized', _external=True))
@bp.route('/authorized')
@bp.oauth.authorized_handler
def authorized(resp):
if resp is None:
flash(u'You denied the request to sign in.')
return redirect(url_for('frontend.index'))
if bp.oauth_type == 'oauth2':
resp['access_token'] = (resp['access_token'], '')
current_user[bp.name] = resp
current_user.save()
flash('You were signed in to %s' % bp.name.capitalize())
return redirect(url_for('frontend.index'))
@bp.oauth.tokengetter
def get_token(token=None):
if bp.oauth_type == 'oauth2':
return current_user.get(bp.name, None)['access_token']
return current_user.get(bp.name, None)['oauth_token']
我尝试将它们一起放在一个类中,然后导入它们,但遇到各种装饰器的问题(oauth 装饰器出现问题最多)。
2、解决方案
我想出了一种看起来像上述问题的一个好解决方案。请告诉我这是否真的是一个好解决方案。
我决定使用 Flask 类视图。这几乎让我为上述每个主要 API 函数创建了一个类视图:
class APILoginView(View):
decorators = [login_required]
def __init__(self, blueprint):
self.blueprint = blueprint
def dispatch_request(self):
if current_user.get(self.blueprint.name, None):
return redirect(url_for('frontend.index'))
return self.blueprint.oauth.authorize(callback=url_for('.authorized', _external=True))
class APIAuthorizedView(View):
decorators = [login_required]
def __init__(self, blueprint):
self.blueprint = blueprint
def dispatch_request(self, resp):
if resp is None:
flash(u'You denied the request to sign in.')
return redirect(url_for('frontend.index'))
if self.blueprint.api.oauth_type == 'oauth2':
resp['access_token'] = (resp['access_token'], '') #need to make it a tuple for oauth2 requests
current_user[self.blueprint.name] = resp
current_user.save()
flash('You were signed in to %s' % self.blueprint.name.capitalize())
return redirect(url_for('frontend.index'))
这是简单的一步。最棘手的事情是弄清楚如何概括 Flask-OAuthLib 库所需的 tokengetter。以下是我的最终结果:
class APIToken():
def __init__(self, blueprint):
self.blueprint = blueprint
def get_token(self, token=None):
if self.blueprint.api.oauth_type == 'oauth2':
return current_user.get(self.blueprint.name, None)['access_token']
return current_user.get(self.blueprint.name, None)['oauth_token']
最后,创建一个函数在蓝图上注册这些视图:
def registerAPIViews(blueprint):
login_view = APILoginView.as_view('login', blueprint=blueprint)
auth_view = blueprint.oauth.authorized_handler(
APIAuthorizedView.as_view('authorized', blueprint=blueprint))
blueprint.add_url_rule('/', view_func=login_view)
blueprint.add_url_rule('/authorized', view_func=auth_view)
apiToken = APIToken(blueprint)
token_getter = blueprint.oauth.tokengetter(apiToken.get_token)
return blueprint