我正在编写一个python脚本,以使用Microsoft Graph API发送邮件。邮件仅发送到我们AAD中存在的ID。
我写了代码:
from adal import AuthenticationContext
import adal
import string
import urllib
import json
import requests
import pprint
import base64
import mimetypes
API_VERSION = 'beta'
RESOURCE = 'https://graph.microsoft.com'
TENANT = <my tenant ID>
CLIENTID = <Client ID>
CLIENTSECRET = <sescret>
DOMAIN = <mydomain>
# AUTHENTICATION - get access token and update session header
def get_session():
authority_host_url = 'https://login.windows.net'
tenant = TENANT
authority_url = authority_host_url + '/' + tenant
service_endpoint = "https://graph.microsoft.com/"
client_id = CLIENTID
client_secret = CLIENTSECRET
# Retrieves authentication tokens from Azure Active Directory and creates a new AuthenticationContext object.
context = AuthenticationContext(authority_url, validate_authority=True, cache=None, api_version=None, timeout=300, enable_pii=False)
# Gets a token for a given resource via client credentials.
token_response = context.acquire_token_with_client_credentials(service_endpoint, client_id, client_secret)
# Create a persistent session with the access token
session = requests.Session()
session.headers.update({'Authorization': f'Bearer {token_response["accessToken"]}', 'SdkVersion': 'python-adal', 'x-client-SKU': 'DynaAdmin'})
#session.headers.update({'Authorization': f'Bearer {token_response["accessToken"]}'})
return session
def build_uri(url):
if 'https' == urllib.parse.urlparse(url).scheme:
return url
result = urllib.parse.urljoin(f'{RESOURCE}/{API_VERSION}/', url.lstrip('/'))
print("\nURL:\n")
print(result)
return result
def sendmail(*, session, subject=None, recipients=None, body='',
content_type='HTML', attachments=None):
"""Helper to send email from current user.
session = user-authenticated session for graph API
subject = email subject (required)
recipients = list of recipient email addresses (required)
body = body of the message
content_type = content type (default is 'HTML')
attachments = list of file attachments (local filenames)
Returns the response from the POST to the sendmail API.
"""
# Verify that required arguments have been passed.
if not all([session, subject, recipients]):
raise ValueError('sendmail(): required arguments missing')
# Create recipient list in required format.
recipient_list = [{'EmailAddress': {'Address': address}}
for address in recipients]
# Create list of attachments in required format.
attached_files = []
if attachments:
for filename in attachments:
b64_content = base64.b64encode(open(filename, 'rb').read())
mime_type = mimetypes.guess_type(filename)[0]
mime_type = mime_type if mime_type else ''
attached_files.append( \
{'@odata.type': '#microsoft.graph.fileAttachment',
'ContentBytes': b64_content.decode('utf-8'),
'ContentType': mime_type,
'Name': filename})
# Create email message in required format.
email_msg = {'Message': {'Subject': subject,
'Body': {'ContentType': content_type, 'Content': body},
'ToRecipients': recipient_list,
'Attachments': attached_files},
'SaveToSentItems': 'true'}
print("\nBody:\n")
print(email_msg)
# Do a POST to Graph's sendMail API and return the response.
resp = session.post(build_uri('/users/8368b7b5-b337ac267220/sendMail'), data=email_msg, stream=True, headers={'Content-Type': 'application/json'})
return resp
# ------------ RUN CODE ------------
session = get_session()
myRecipients = "sea.chen@mydomain.com;anj.dy@mydomain.com"
response = sendmail(session=session,
subject= "hai",
recipients=myRecipients.split(';'),
body="hai this is a new mail")
print("\nRequest headers: \n")
print(response.request.headers)
print("\nResponse: \n")
print(response)
print(response.text)
我得到的输出是:
Body:
{'Message': {'Subject': 'hai', 'Body': {'ContentType': 'HTML', 'Content': 'hai this is a new mail'}, 'ToRecipients': [{'EmailAddress': {'Address': 'sea.chen@mydomain.com'}}, {'EmailAddress': {'Address': 'anj.dy@mydomain.com'}}], 'Attachments': []}, 'SaveToSentItems': 'true'}
URL:
https://graph.microsoft.com/beta/users/8368b7b5-b337ac267220/sendMail
Request headers:
{'User-Agent': 'python-requests/2.22.0', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 'Authorization': 'Bearer eyJ0eXAiOiJKV1QiLCJub25jZSI6IkFRQUJBQUFBQUFEQ29NcGpKWHJ4VHE5Vkc5dGUtN0ZYVzRQOWpybFRZWXdIVmZieWJxd0dUVUtNRThqUG5Va0hFSlowU2JyUFRaaDlaSjdURklVMjRwV1RpOTQxZU5kaXpIeHdXdDk0ODNlYmY1OWE5IiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvOWFmYjFmOGEtMjE5Mi00NWJhLWIwYTEtNmIxOTNjNzU4ZTI0LyIsIm9pZCI6ImI3NDY4ZDQxLTkxMWMtNGU4ZS1iMzA4LWY5NmM1ZGQ4MDVmMyIsInJvbGVzIjpbIlVzZXIuUmVhZFdyaXRlLkFsbCIsIkdyb3VwLlJlYWQuQWxsIiwiRGlyZWN0b3J5LlJlYWRXcml0ZS5BbGwiLCJHcm91cC5SZWFkV3JpdGUuQWxsIiwiRGlyZWN0b3J5LlJlYWQuQWxsIiwiVXNlci5SZWFkLkFsbCIsIk1haWwuU2VuZCJdLCJzdWIiOiJiNzQ2OGQ0MS05MTFjLTRlOGUtYjMwOC1mOTZjNWRkODA1ZjMiLCJ0aWQiOiI5YWZiMWY4YS0yMTkyLTQ1YmEtYjBhMS02YjE5M2M3NThlMjQiLCJ1dGkiOiJWX1d6UHJjUDhrQ000MGNPM0xZZkFBIiwidmVyIjoiMS4wIiwieG1zX3RjZHQiOjE0NTY0Nzc5MzB9.Nq0qdmfd4qAQWdnaFLVNKYWQ__t52jRYC09IsDlrSOoAhZU6d2M6ePAAaKFSR-Ss_NJ4o21FAbxRM8mRUumW3n1TFvARN0FDzjtZMG9mgIrPmYndfRQtcD3s7f5Q5JOQdtd5QDRVhPqVRNAwmC-o_TW5mm0p40oIR2Mc2MN_MB3dp-_0xH7fN3xsPzWi9yRR1-iHnvEjLmhKecY5pxCBO3RW5QVcAR6DH6kw0koV49cmjpIu-_gau4SFlF4kFdwEVXdv1jTeJj91lA02Ar9tR-2hQiPOaqsloSmKpaH0Tb4LwGQJBk2O8fiwy5Sv2NoKbi6QE2EPFck7jZPVBDh35g', 'SdkVersion': 'python-adal', 'x-client-SKU': 'DynaAdmin', 'Content-Type': 'application/json', 'Content-Length': '90'}
Response:
<Response [400]>
{
"error": {
"code": "BadRequest",
"message": "Unable to read JSON request payload. Please ensure Content-Type header is set and payload is of valid JSON format.",
"innerError": {
"request-id": "26ef0c0b-c362-401c-b8ed-48755a45d086",
"date": "2019-06-24T07:10:53"
}
}
}
我得到的错误是:
无法读取JSON请求有效负载。请确保Content-Type标头 已设置,并且有效载荷具有有效的JSON格式。
因此,我在Graph Explorer中尝试了该请求,并使用了与上面输出中打印的相同的URL和正文。从Graph Explorer发送的请求成功,并且邮件已发送到正确的收件人。这意味着content-body
是有效的。
从上面的输出中可以明显看出,承载令牌和'Content-Type': 'application/json'
已作为标头传递。
然后为什么在运行脚本时出现错误? 谁能帮我吗?
答案 0 :(得分:2)
很可能是您的POST正文格式不正确。当我使用requests
库执行此操作时,我发现需要像这样设置data
:
data = json.dumps(email_msg)
data
的默认设置是表单编码,而不是作为JSON传递。看看https://docs.microsoft.com/outlook/rest/python-tutorial#contents-of-tutorialoutlookservicepy