You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Python调用Google Workspace API获取用户时遇400错误求助

Google Workspace Directory API调用返回HTTP 400 "Invalid Input"错误排查

问题描述

调用Google Workspace Directory API获取用户列表时收到HTTP 400错误,错误信息如下:

"exception": "googleapiclient.errors.HttpError: <HttpError 400 when requesting https://admin.googleapis.com/admin/directory/v1/users?customer=my_customer&query=isSuspended%3DFalse&maxResults=100&orderBy=email&viewType=admin_view&projection=basic&showDeleted=False&alt=json returned "Invalid Input". Details: "[{'message': 'Invalid Input', 'domain': 'global', 'reason': 'invalid'}]">"

使用的代码如下:

import frappe
import json
from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
SUBJECT = 'workspace_email'
CUSTOMER_ID = 'my_customer'

@frappe.whitelist(allow_guest=True)
def main():
    credentials = service_account.Credentials.from_service_account_file(
        'path_to_my_config.json', scopes=SCOPES
    )
    credentials = credentials.with_subject(SUBJECT)
    admin = build('admin', 'directory_v1', credentials=credentials)
    results = admin.users().list(
        customer=CUSTOMER_ID,
        query='isSuspended=False',
        maxResults=100,
        orderBy='email',
        viewType='admin_view',
        projection='basic',
        showDeleted=False,
    ).execute()
    print(json.dumps(results, indent=4))

if __name__ == '__main__':
    main()

已尝试重新生成服务密钥,并为主体账号分配了Owner、Service Account Admin、Service Account Token Creator、Service Account User角色,仍无法解决问题。


解决方案

1. 核心问题:参数冲突

viewType和projection参数不能同时使用,这是触发"Invalid Input"错误的直接原因。根据Google Directory API规则:

  • 指定viewType=admin_view时,API会自动采用对应视图的默认数据投影规则,无需额外设置projection
  • 若要使用projection参数筛选返回字段,必须移除viewType参数

2. 修正后的代码示例

方案一:保留projection=basic,移除viewType

import frappe
import json
from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
SUBJECT = 'workspace_email'
CUSTOMER_ID = 'my_customer'

@frappe.whitelist(allow_guest=True)
def main():
    credentials = service_account.Credentials.from_service_account_file(
        'path_to_my_config.json', scopes=SCOPES
    )
    credentials = credentials.with_subject(SUBJECT)
    admin = build('admin', 'directory_v1', credentials=credentials)
    results = admin.users().list(
        customer=CUSTOMER_ID,
        query='isSuspended=False',
        maxResults=100,
        orderBy='email',
        projection='basic',
        showDeleted=False,
    ).execute()
    print(json.dumps(results, indent=4))

if __name__ == '__main__':
    main()

方案二:保留viewType=admin_view,移除projection

import frappe
import json
from google.oauth2 import service_account
from googleapiclient.discovery import build

SCOPES = ['https://www.googleapis.com/auth/admin.directory.user.readonly']
SUBJECT = 'workspace_email'
CUSTOMER_ID = 'my_customer'

@frappe.whitelist(allow_guest=True)
def main():
    credentials = service_account.Credentials.from_service_account_file(
        'path_to_my_config.json', scopes=SCOPES
    )
    credentials = credentials.with_subject(SUBJECT)
    admin = build('admin', 'directory_v1', credentials=credentials)
    results = admin.users().list(
        customer=CUSTOMER_ID,
        query='isSuspended=False',
        maxResults=100,
        orderBy='email',
        viewType='admin_view',
        showDeleted=False,
    ).execute()
    print(json.dumps(results, indent=4))

if __name__ == '__main__':
    main()

3. 额外检查项

  • 确认SUBJECT变量替换为实际的Google Workspace管理员邮箱(需拥有用户数据读取权限)
  • 检查服务账号是否已在Google Workspace管理控制台启用域范围委派,并授权了指定的SCOPES权限

内容的提问来源于stack exchange,提问作者Verckys Orwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 20:33:40