如何使用 Microsoft API

前言

来啦老铁!

由于工作上需要操作 Outlook 日历,想通过 API 来操作以提高稳定性及降低复杂度,因此对 Microsoft API 有所接触,所以今天咱们一起来学点工具相关的,他就是:

  • 如何使用 Microsoft API

注:Microsoft API 跟 Google API 一样,都有很多,而我仅以操作 Outlook 日历为例进行学习记录~

对如何使用 Google API 感兴趣的朋友可参考往期文章:如何使用 Google API

学习路径

  1. 创建 Microsoft 应用;
  2. 创建 Microsoft 秘钥;
  3. Token 配置;
  4. 更新 Authentication 配置;
  5. 增加/更新接口权限;
  6. 生成 Access Token;
  7. 演示如何使用 Microsoft API 操作 Outlook 日历;
  8. API 调用 typescript 代码演示(简单演示,仅供参考);

1. 创建 Microsoft 应用;

非第一次使用 Microsoft API

  • 直接访问:RegisteredApps,点击自己创建的 Microsoft 应用进入操作即可;
RegisteredApps

第一次使用 Microsoft API

  • 浏览器访问 https://apps.dev.microsoft.com

  • 使用 Microsoft 账号(如 Outlook 邮箱账号密码)登录;

  • 登录后会看到这样的页面:


    登录 https://apps.dev.microsoft.com
  • 点击 “Add an app in the Azure portal” 按钮,进入注册 Microsoft 应用的流程;
    a. 进入注册 Microsoft 应用页面;

注册 Microsoft 应用页面

b. 点击“+ New registration” 按钮;

“+ New registration” 按钮

c. 填写应用名 Name、支持的账号类型 Support account types(不用担心,后续可修改);

填写 Microsoft 应用注册信息

d. 点击页面左下角的 Register 按钮进行注册;

Register 按钮

e. 几秒钟就能创建出我们的 Microsoft 应用啦~

Microsoft 应用信息

2. 创建 Microsoft 秘钥;

  • 点击上图 Microsoft 应用信息上的“Add a certificate or secret” 入口进入创建秘钥流程;
“Add a certificate or secret” 入口
  • 点击“+ New client secret” 按钮创建秘钥;


    “+ New client secret” 按钮
  • 填写秘钥描述信息、有效时长;

填写秘钥描述信息、有效时长
  • 点击左下角的“Add” 按钮创建秘钥;
“Add” 按钮
  • 秘钥创建成功:
秘钥创建成功

特别注意:秘钥值只显示一次,而且这个秘钥在后续创建 access token 的时候是必要的,十分重要,我们需要记住;

秘钥值

3. Token 配置;

  • 点击左侧的 “Token configuration” 入口,然后点击“+ Add optional claim”按钮开始进行 Token 配置;
Token 配置入口
  • 在打开的右侧弹框中选择 Token type 为 Access,并勾选所有 Claim;
选择 Token type
  • 点击弹框内的左下角“Add”按钮,


    “Add”按钮
  • 勾选 Turn on the Microsoft Graph email...,然后点击“Add”按钮添加 claim;

勾选
  • claim 添加完成;
claim 添加完成

4. 更新 Authentication 配置;

  • 点击左侧栏的 “Authentication” 入口,修改 Advanced settings 下的 Enable the following mobile and desktop flows 为 Yes;
Enable the following mobile and desktop flows

5. 增加/更新接口权限;

  • 点击左侧栏的 “API permissions”,然后点击“+ Add a permission” 按钮;
“+ Add a permission” 按钮
  • 在打开的 Request API permissions 右弹框中,选择 Microsoft Graph 类型的 API;
选择 Microsoft Graph 类型的 API
  • 接着选择 Microsoft Graph 类型的 API 的权限类型为 Application permissions,这个至关重要;
Microsoft Graph - Application permissions
  • 搜索添加 API 权限;
添加 API 权限
  • 以操作 Outlook 日历为例,需添加以下 API 权限:
操作 Outlook 日历 - API 权限们
  • 此时,有些 API 权限的 Admin consent required 值为 Yes,说明需要点击“Grant admin consent for...” 按钮,即管理员授权同意,而这个按钮需要登录的账号有 ADMIN 权限,这个需要特别注意!
Grant admin consent for...
  • 管理员授权同意;
管理员授权同意
  • 管理员授权同意后:
管理员授权同意后

注:如果未获得管理员授权同意,则后续在调用 Outlook 日历 API 的时候会报权限错误:

{
    "error": {
        "code": "ErrorAccessDenied",
        "message": "Access is denied. Check credentials and try again."
    }
}

6. 生成 Access Token;

  • 我们可以使用 postman 等工具,或写代码去生成 Access Token,以 postman 工具为例:
生成 Access Token
其中:

a. url: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
b. tenant 来源: 见下图;
b. client_id 来源: 见下图;
c. client_secret: 之前说的要记住的秘钥;

信息来源

7. 演示如何使用 Microsoft API 操作 Outlook 日历;

对于 API 使用,Microsoft 有很庞大的(全英文,不好看懂,看起来慢)文档,文档链接:

而我更推荐使用 Microsoft 提供的接口试用工具:

我们可以轻松的搜索 API,并进行测试:

graph-explorer
注:登录 https://developer.microsoft.com/en-us/graph/graph-explorer 站点后可用本账号发送真实用户的请求;
登录入口
  • 发送真实用户的请求;
image.png
  • 然后我们将请求转移到 postman 试下,Access Token 试用步骤 6 生成的 Access Token,结果出现了意想不到的错误:
{
    "error": {
        "code": "BadRequest",
        "message": "/me request is only valid with delegated authentication flow.",
        "innerError": {
            "date": "2022-05-07T16:01:33",
            "request-id": "xxxxxxxxxxxxxxxxxxxxxxx",
            "client-request-id": "yyyyyyyyyyyyyyyyyyyyy"
        }
    }
}
转移到 postman 调用 API

接下来这个非常重要!!!

这个问题查阅了很多资料,最终在一个网友的答案中找到答案,即,不能用 /me,而应该用 /user/{邮箱地址} 取代,如 url 为:

不能用 /me
  • 接着我们演示如何使用 Microsoft API 操作 Outlook 日历,获取所有日历事件详情:
获取所有日历详情
  • 创建新的 Outlook 日历事件:
创建新的 Outlook 日历事件
request body 我们可以从上一步获取到的日历事件详情获得参考,替换信息~

8. API 调用 typescript 代码演示(简单演示,仅供参考);

private async getMicrosoftAccessToken(directoryId = "xxxxxxxxxxxxx", client_id = "yyyyyyyyyyyyyyy", client_secret = "xyxyxyxyxyyxyxyxyxyyxyxyxyyxxyxyxyyx") {
    console.log("getMicrosoftAccessToken...");
    const response = await axios.request({
      method: 'POST',
      url: `https://login.microsoftonline.com/${directoryId}/oauth2/v2.0/token`,
      headers: {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Content-Type": "application/x-www-form-urlencoded"
      },
      data: `client_id=${client_id}&client_secret=${client_secret}&grant_type=client_credentials&scope=https://graph.microsoft.com/.default`
    });
    expect(response.status).eq(200);
    expect(response.data["token_type"]).eq("Bearer");
    expect(response.data["access_token"]).not.empty;
    return response.data["access_token"];
  }

  async inviteAudienceViaMockingCalendarEvent(params: {
    webinarSubject: string,
    inviterUserName: string,
    inviterDisplayName: string,
    inviterEmail: string,
    inviteeUserName: string,
    inviteeDisplayName: string,
    inviteeEmail: string,
    audienceJoinUri: string,
    startTime: string,
    endTime: string,
    timeZone?: string,
    dateString?: string,
    timeString?: string
  }) {
    const dateString = params.dateString || "Thursday, May 5, 2022";
    const timeString = params.timeString || "10:00 PM - 11:00 PM Asia/Shanghai";
    const data = {
      subject: params.webinarSubject,
      bodyPreview: `${params.inviterUserName} has invited you to attend the following webinar:${params.webinarSubject}${dateString}${timeString}Webinar link:${params.audienceJoinUri}`,
      body: {
        contentType: "html",
        content: `<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"></head><body><div class=\"BodyFragment\"><font size=\"2\"><span style=\"font-size:11pt\"><div class=\"PlainText\">${params.inviterUserName} has invited you to attend the following webinar:<br>${params.webinarSubject}<br><br>${dateString}<br>${timeString}<br><br>Webinar link:<br><a href=\"${params.audienceJoinUri}\">${params.audienceJoinUri}</a><br><br></div></span></font></div></body></html>`
      },
      start: {
        dateTime: params.startTime,
        timeZone: params.timeZone || "UTC"
      },
      end: {
        dateTime: params.endTime,
        timeZone: params.timeZone || "UTC"
      },
      location: {
        displayName: params.audienceJoinUri,
        locationType: "default",
        uniqueId: params.audienceJoinUri,
        uniqueIdType: "private"
      },
      attendees: [
        {
          type: "required",
          status: {
            response: "none",
            time: "0001-01-01T00:00:00Z"
          },
          emailAddress: {
            name: params.inviterDisplayName,
            address: params.inviterEmail
          }
        },
        {
          type: "required",
          status: {
            response: "none",
            time: "0001-01-01T00:00:00Z"
          },
          emailAddress: {
            name: params.inviteeDisplayName,
            address: params.inviteeEmail
          }
        }
      ],
      organizer: {
        emailAddress: {
          name: params.inviterDisplayName,
          address: params.inviterEmail
        }
      }
    }
    const accessToken = await this.getMicrosoftAccessToken();

    console.log("inviteAudienceViaMockingCalendarEvent...");
    const response = await axios.request({
      method: 'POST',
      url: `https://graph.microsoft.com/v1.0/users/${params.inviteeEmail}/events`,
      headers: {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Content-Type": "application/json",
        "Authorization": `Bearer ${accessToken}`
      },
      data
    });
    expect(response.status).eq(201);
    expect(response.data["subject"]).eq(params.webinarSubject);
    expect(response.data["iCalUId"]).not.empty;
  }

好了,对如何使用 Microsoft API 的探索我们就先到这了,篇幅较长,不过大部分是一次性准备工作,后续的 API 使用就很简单了~

当然,Microsoft API 众多,笔者也没有更多 API 使用的经验和探索的时间,不过我相信,其他 API 也是类似的使用方法,应该不在话下,未来有更多收获我再记录下来~

能力有限,欢迎指正、互相交流,感谢~

如果本文对您有帮助,麻烦点赞、关注!

感谢~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361

推荐阅读更多精彩内容