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

如何通过Microsoft Graph API筛选包含指定项目名称的服务主体并返回其应用ID

如何通过Microsoft Graph API筛选包含指定项目名称的服务主体并返回其应用ID

你遇到的问题其实是对Graph API的$filter语法细节没掌握到位——contains函数其实是支持的,但需要配合正确的请求头和参数,同时要注意大小写处理和URL编码的问题。我来帮你修正代码并解释清楚:

核心要点说明

  1. 正确的筛选语法
    Graph API v1.0支持contains函数做字符串模糊匹配,如果你需要大小写不敏感的匹配,可以结合tolower函数,避免因为大小写差异漏匹配。比如要匹配包含目标项目名称的displayName,筛选表达式可以写成:

    contains(tolower(displayName), tolower('{你的项目名称}'))
    

    如果你需要更精确匹配xxx - PROJECT NAME xxx这种带分隔符的场景,也可以把分隔符加入筛选条件:

    contains(tolower(displayName), tolower(' - {你的项目名称} '))
    
  2. 必须的请求配置
    你代码里已经加了ConsistencyLevel: eventual和$count=true,这两点非常关键——因为contains这类模糊查询属于高级筛选,必须开启这两个配置才能生效,否则会返回错误。

  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 16:27:59