使用图形 API 的发送邮件请求时出现"BadRequest"错误Microsoft



我正在编写一个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 请求有效负载。请确保内容类型标头 已设置,并且有效负载为有效的 JSON 格式。

所以我在 Graph 资源管理器中尝试了该请求,并使用了我在上述输出中打印的相同 URL 和正文。从 Graph 资源管理器发送的请求成功,邮件已发送给正确的收件人。这意味着content-body有效。

从上面的输出中可以明显看出,持有者令牌和'Content-Type': 'application/json'作为标头传递。

那为什么我在运行脚本时收到错误?谁能帮我?

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

data = json.dumps(email_msg)

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

感谢您分享这个。当我尝试让图形 API 向 Teams 发送一对一聊天消息时,我遇到了不同的问题Microsoft。我可以使用http.client运行以下代码,并在聊天中获取大部分消息。

import http.client
headers = {'Content-type':'application/json',
            "Authorization": f"Bearer {getGraphAccessToken()}"
        }
  
body = {
       "body": {
          "contentType": "text",
         "charset" : "utf-8",
          "content": text
  }}
  connection = http.client.HTTPSConnection("graph.microsoft.com")
  connection.request("POST",f"/v1.0/chats/{chatId}/messages",headers=headers, body = str(body))

但是,当我在文本中有某些表情符号时,我收到以下错误:"无法读取 JSON 请求有效负载。请确保设置了内容类型标头,并且有效负载具有有效的 JSON 格式。

通过将库切换到请求,我能够发送带有特殊表情符号的消息来工作。

import requests
import json
headers = {'Content-type':'application/json',
            "Authorization": f"Bearer {getGraphAccessToken()}"
        }
  
body = {
     "body": {
        "contentType": "text",
        "content": text
}}
requests.post(f"https://graph.microsoft.com/v1.0/chats/{chatId}/messages",headers=headers,data =json.dumps(body) )

最新更新