使用 microsoftgraph API 和 Django 处理 webhook
Processing a webhook with the microsoftgraph API and Django
我目前正在开发一个简单的 Web 应用程序,它通过 Microsoft Graph API 连接到 Outlook 365 帐户。
在此应用程序中,我想设置一个 webhook 来处理收件箱中收到的电子邮件。
但是,为此,我需要检索令牌才能处理电子邮件。
这是我当前的 Django 代码:
@csrf_exempt
def webhook(request):
validation_token = str(request)
validation_token = validation_token.replace(
"<WSGIRequest: POST '/app/webhook/?validationToken=", "")
validation_token = validation_token.replace(
"HTTP/1.1", "").replace("'>", "")
if len(request.body) == 0:
request.session['validation_token'] = validation_token
return HttpResponse(validation_token, content_type="text/plain")
else:
data = json.loads(request.body)
print('received notification(s) !')
notifications = data['value']
request.session['notifications'] = notifications
return HttpResponseRedirect(reverse('app:categorize'), status=202)
我重定向到的分类视图(在 views.py
中)应该处理传入通知(消息接收或更新)。我没有在 webhook 视图中处理它的原因是因为我需要更新一个应该已经存储在会话对象中的令牌。然而,也许因为 request
是传入的 POST 查询,令牌未存储在会话中。
def categorize(request):
access_token, refresh_token, expiration = get_access_token(
request, request.build_absolute_uri(reverse('app:gettoken')))
notifications = request.session.get('notifications')
if notifications is not None:
account = OutlookAccount(
access_token=access_token,
refresh_token=refresh_token,
token_expiration=expiration
)
for resource in notifications:
resource_data = resource['resourceData']
client_state = resource['clientState']
if client_state == 'CLIENT_STATE':
do_something(resource_data)
return HttpResponseRedirect(reverse('app:mail'))
用于检索查询(从另一个模块导入)的 python 函数是
def get_access_token(request, redirect_uri):
try:
current_token = request.session['access_token']
expiration = request.session['token_expires']
refresh_token = request.session['refresh_token']
now = int(time.time())
if (current_token and now < expiration):
# Token still valid
return current_token, refresh_token, expiration
else:
# Token expired
refresh_token = request.session['refresh_token']
new_tokens = get_token_from_refresh_token(refresh_token, redirect_uri)
# Update session
# expires_in is in seconds
# Get current timestamp (seconds since Unix Epoch) and
# add expires_in to get expiration time
# Subtract 5 minutes to allow for clock differences
expiration = int(time.time()) + new_tokens['expires_in'] - 300
# Save the token in the session
request.session['access_token'] = new_tokens['access_token']
request.session['refresh_token'] = new_tokens['refresh_token']
request.session['token_expires'] = expiration
return new_tokens['access_token'], new_tokens['refresh_token'], expiration
except KeyError:
return None, None, None
gettoken
视图提供端点以检索令牌。
def gettoken(request):
auth_code = request.GET['code']
redirect_uri = request.build_absolute_uri(reverse('app:gettoken'))
token = get_token_from_code(auth_code, redirect_uri)
access_token = token['access_token']
refresh_token = token['refresh_token']
expires_in = token['expires_in']
expiration = int(time.time()) + expires_in - 300
request.session['access_token'] = access_token
request.session['refresh_token'] = refresh_token
request.session['token_expires'] = expiration
return HttpResponseRedirect(reverse('app:mail'))
我意识到函数 get_access_token
与之前的视图有点多余,但我不确定如何仅使用 gettoken 视图来检索身份验证令牌。
总而言之,我的主要问题是弄清楚如何访问通常在手动登录后存储的 access_token
、refresh_token
属性。然而,这些在 webhook 视图中不可访问(也许是正确的)。
我需要的是一种临时重定向到 gettoken
视图的方法,将令牌存储在会话中,并在 webhook
视图中完成处理。
很抱歉,如果这没有意义,我只是在学习 Django,而且我根本不是一名 Web 开发人员。
您无法从会话中提取令牌,因为 Web 挂钩不是来自用户的浏览器会话。它来自远程计算机上的 Microsoft Graph。因此,它看起来像是另一个客户端访问了您的后端,而不是来自给定用户会话的消息。
如果您需要根据 webhook 通知采取行动,您需要将 refresh_token
存储在会话的 外部 和适当的系统中将给定的通知连接到正确的用户。有了这些,您就可以接收通知、获取新令牌并代表用户调用 Microsoft Graph。
我目前正在开发一个简单的 Web 应用程序,它通过 Microsoft Graph API 连接到 Outlook 365 帐户。 在此应用程序中,我想设置一个 webhook 来处理收件箱中收到的电子邮件。 但是,为此,我需要检索令牌才能处理电子邮件。
这是我当前的 Django 代码:
@csrf_exempt
def webhook(request):
validation_token = str(request)
validation_token = validation_token.replace(
"<WSGIRequest: POST '/app/webhook/?validationToken=", "")
validation_token = validation_token.replace(
"HTTP/1.1", "").replace("'>", "")
if len(request.body) == 0:
request.session['validation_token'] = validation_token
return HttpResponse(validation_token, content_type="text/plain")
else:
data = json.loads(request.body)
print('received notification(s) !')
notifications = data['value']
request.session['notifications'] = notifications
return HttpResponseRedirect(reverse('app:categorize'), status=202)
我重定向到的分类视图(在 views.py
中)应该处理传入通知(消息接收或更新)。我没有在 webhook 视图中处理它的原因是因为我需要更新一个应该已经存储在会话对象中的令牌。然而,也许因为 request
是传入的 POST 查询,令牌未存储在会话中。
def categorize(request):
access_token, refresh_token, expiration = get_access_token(
request, request.build_absolute_uri(reverse('app:gettoken')))
notifications = request.session.get('notifications')
if notifications is not None:
account = OutlookAccount(
access_token=access_token,
refresh_token=refresh_token,
token_expiration=expiration
)
for resource in notifications:
resource_data = resource['resourceData']
client_state = resource['clientState']
if client_state == 'CLIENT_STATE':
do_something(resource_data)
return HttpResponseRedirect(reverse('app:mail'))
用于检索查询(从另一个模块导入)的 python 函数是
def get_access_token(request, redirect_uri):
try:
current_token = request.session['access_token']
expiration = request.session['token_expires']
refresh_token = request.session['refresh_token']
now = int(time.time())
if (current_token and now < expiration):
# Token still valid
return current_token, refresh_token, expiration
else:
# Token expired
refresh_token = request.session['refresh_token']
new_tokens = get_token_from_refresh_token(refresh_token, redirect_uri)
# Update session
# expires_in is in seconds
# Get current timestamp (seconds since Unix Epoch) and
# add expires_in to get expiration time
# Subtract 5 minutes to allow for clock differences
expiration = int(time.time()) + new_tokens['expires_in'] - 300
# Save the token in the session
request.session['access_token'] = new_tokens['access_token']
request.session['refresh_token'] = new_tokens['refresh_token']
request.session['token_expires'] = expiration
return new_tokens['access_token'], new_tokens['refresh_token'], expiration
except KeyError:
return None, None, None
gettoken
视图提供端点以检索令牌。
def gettoken(request):
auth_code = request.GET['code']
redirect_uri = request.build_absolute_uri(reverse('app:gettoken'))
token = get_token_from_code(auth_code, redirect_uri)
access_token = token['access_token']
refresh_token = token['refresh_token']
expires_in = token['expires_in']
expiration = int(time.time()) + expires_in - 300
request.session['access_token'] = access_token
request.session['refresh_token'] = refresh_token
request.session['token_expires'] = expiration
return HttpResponseRedirect(reverse('app:mail'))
我意识到函数 get_access_token
与之前的视图有点多余,但我不确定如何仅使用 gettoken 视图来检索身份验证令牌。
总而言之,我的主要问题是弄清楚如何访问通常在手动登录后存储的 access_token
、refresh_token
属性。然而,这些在 webhook 视图中不可访问(也许是正确的)。
我需要的是一种临时重定向到 gettoken
视图的方法,将令牌存储在会话中,并在 webhook
视图中完成处理。
很抱歉,如果这没有意义,我只是在学习 Django,而且我根本不是一名 Web 开发人员。
您无法从会话中提取令牌,因为 Web 挂钩不是来自用户的浏览器会话。它来自远程计算机上的 Microsoft Graph。因此,它看起来像是另一个客户端访问了您的后端,而不是来自给定用户会话的消息。
如果您需要根据 webhook 通知采取行动,您需要将 refresh_token
存储在会话的 外部 和适当的系统中将给定的通知连接到正确的用户。有了这些,您就可以接收通知、获取新令牌并代表用户调用 Microsoft Graph。