如何通过Microsoft Graph API筛选包含指定项目名称的服务主体并返回其应用ID
如何通过Microsoft Graph API筛选包含指定项目名称的服务主体并返回其应用ID
你遇到的问题其实是对Graph API的$filter语法细节没掌握到位——contains函数其实是支持的,但需要配合正确的请求头和参数,同时要注意大小写处理和URL编码的问题。我来帮你修正代码并解释清楚:
核心要点说明
正确的筛选语法
Graph API v1.0支持contains函数做字符串模糊匹配,如果你需要大小写不敏感的匹配,可以结合tolower函数,避免因为大小写差异漏匹配。比如要匹配包含目标项目名称的displayName,筛选表达式可以写成:contains(tolower(displayName), tolower('{你的项目名称}'))如果你需要更精确匹配
xxx - PROJECT NAME xxx这种带分隔符的场景,也可以把分隔符加入筛选条件:contains(tolower(displayName), tolower(' - {你的项目名称} '))必须的请求配置
你代码里已经加了ConsistencyLevel: eventual和$count=true,这两点非常关键——因为contains这类模糊查询属于高级筛选,必须开启这两个配置才能生效,否则会返回错误。URL编码处理
项目名称里可能包含空格、特殊字符,直接拼到URL里会导致请求失败,所以需要用Python的urllib.parse.quote对筛选条件进行编码。
修改后的完整代码
import requests import urllib.parse import logging logger = logging.getLogger(__name__) def get_service_principal_app_id(project_name, bearer_token): # 标准化项目名称,统一转为小写避免大小写问题 normalized_project = project_name.strip().lower() # 构造大小写不敏感的模糊筛选表达式 filter_expression = f"contains(tolower(displayName), tolower('{normalized_project}'))" # 对筛选表达式做URL编码,处理特殊字符 filter_query = urllib.parse.quote(filter_expression) url = f"https://graph.microsoft.com/v1.0/servicePrincipals?$filter={filter_query}&$count=true" headers = { "Authorization": f"Bearer {bearer_token}", # 修正Authorization的正确格式 "ConsistencyLevel": "eventual" # GET请求无需Content-Type头,有特殊需求可以再添加 } # 分页处理 while url: response = requests.get(url, headers=headers) if response.status_code == 200: service_principals = response.json().get('value', []) for sp in service_principals: # 保留你原本的精确格式检查,确保匹配"xxx - 项目名称 xxx"的结构 display_name_parts = sp['displayName'].split(" - ") if len(display_name_parts) > 1 and display_name_parts[1].strip().lower() == normalized_project: service_principal_id = sp['id'] application_id = sp['appId'] logger.info(f"Service Principal ID: {service_principal_id}") logger.info(f"Application ID: {application_id}") return service_principal_id, application_id url = response.json().get('@odata.nextLink', None) else: logger.error(f"Error: {response.status_code} - {response.text}") return None, None logger.error("未找到符合格式要求的服务主体(名称需包含分隔符及指定项目名)") return None, None
代码关键修改点
- 修正了
Authorization头的格式,必须是Bearer {token}的形式; - 加入URL编码,避免项目名称中的特殊字符破坏请求URL;
- 用
tolower+contains组合实现大小写不敏感匹配,提升兼容性; - 移除了不必要的
Content-Type头(GET请求不需要该参数)。
如果你的场景需要严格匹配分隔符后的项目名称,也可以把筛选表达式写得更精准,比如:
filter_expression = f"tolower(split(displayName, ' - ')[1]) eq tolower('{normalized_project}')"
不过这种写法依赖displayName的格式完全符合xxx - yyy的结构,一旦格式有变化就会失效,所以结合本地检查的方式会更稳妥。
备注:内容来源于stack exchange,提问作者Jimmy
相关产品推荐
相关产品推荐

