使用Microsoft Graph API的SendMail请求时出现“ BadRequest”错误

时间:2019-06-24 07:34:35

标签: python azure-active-directory microsoft-graph

我正在编写一个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'已作为标头传递。

然后为什么在运行脚本时出现错误? 谁能帮我吗?

1 个答案:

答案 0 :(得分:2)

很可能是您的POST正文格式不正确。当我使用requests库执行此操作时,我发现需要像这样设置data

data = json.dumps(email_msg)

data的默认设置是表单编码,而不是作为JSON传递。看看https://docs.microsoft.com/outlook/rest/python-tutorial#contents-of-tutorialoutlookservicepy