已建立Gmail API连接但无法发送邮件的问题解决
修复Gmail API发送邮件的两个错误
错误1:Invalid value at 'message' (raw), Starting an object on a scalar field
问题原因
你的GoogleMailClient.send_email方法内部已经会把传入参数包装成{'raw': message_body},但调用时你传入的是已经包含raw键的字典,导致最终提交给API的请求体变成了:
{'raw': {'raw': '你的base64编码内容'}}
Gmail API要求raw字段是字符串类型,而非嵌套对象,因此触发400错误。
修复方案
调用send_email时,直接传入base64编码后的字符串,而非字典:
# 去掉构建message字典的步骤,直接传message_base64 gmail_client.send_email(message_base64)
错误2:'GoogleMailClient' object has no attribute 'users'
问题原因
GoogleMailClient类并未暴露users方法,你错误地直接调用gmail_client.users(),正确的API调用逻辑已经封装在类的send_email方法中,类内部通过self.service调用Gmail API的users().messages().send接口。
修复方案
直接使用类中已定义的send_email方法,不要跳过封装直接调用底层API。
完整修复后的代码示例
# 导入必要模块 import email import base64 import os import pickle from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request # 修正缩进后的GoogleMailClient类(原代码方法未缩进属于语法错误) class GoogleMailClient: def __init__(self): self.SCOPES = ['https://www.googleapis.com/auth/gmail.modify'] self.creds = self.get_credentials() self.service = self.build_gmail_service() def get_credentials(self): creds = None if os.path.exists('gmail_token.pickle'): with open('gmail_token.pickle', 'rb') as token: creds = pickle.load(token) if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'credentials.json', self.SCOPES) creds = flow.run_local_server(port=0) with open('gmail_token.pickle', 'wb') as token: pickle.dump(creds, token) return creds def build_gmail_service(self): return build('gmail', 'v1', credentials=self.creds) def send_email(self, message_body): message = {'raw': message_body} self.service.users().messages().send(userId='me', body=message).execute() def list_messages(self): results = self.service.users().messages().list(userId='me').execute() messages = results.get('messages', []) return messages def get_message(self, message_id): message = self.service.users().messages().get(userId='me', id=message_id).execute() return message def mark_as_read(self, message_id): self.service.users().messages().modify(userId='me', id=message_id, body={'removeLabelIds': ['UNREAD']}).execute() def delete_message(self, message_id): self.service.users().messages().delete(userId='me', id=message_id).execute() gmail_client = GoogleMailClient() # 正确的邮件发送代码 subject = "Your Subject Here" body = "Hey, this is your message body." # 创建邮件对象(必须添加From字段,否则API可能拒绝) message = email.message.EmailMessage() message['from'] = "你的Gmail邮箱地址" message['to'] = "XXX@hotmail.com" message['subject'] = subject message.set_content(body) # 转换并编码邮件内容 message_as_string = message.as_string() message_bytes = message_as_string.encode("utf-8") message_base64 = base64.urlsafe_b64encode(message_bytes).decode("utf-8") # 发送邮件 try: gmail_client.send_email(message_base64) print("邮件发送成功!") except Exception as e: print(f"发送失败: {e}")
额外注意事项
- 类方法缩进:原代码中
get_credentials等方法未缩进在类内部,属于语法错误,必须修正。 - From字段必填:发送邮件时必须指定
From字段,否则Gmail API可能拒绝请求。 - 权限范围:当前使用的
gmail.modify权限已包含发送能力,若仍有问题,可尝试替换为https://www.googleapis.com/auth/gmail.send或全权限https://mail.google.com/。
内容的提问来源于stack exchange,提问作者user20777937
相关产品推荐
相关产品推荐

