前言
来啦老铁!
由于工作上需要操作 Outlook 日历,想通过 API 来操作以提高稳定性及降低复杂度,因此对 Microsoft API 有所接触,所以今天咱们一起来学点工具相关的,他就是:
-
如何使用 Microsoft API
注:Microsoft API 跟 Google API 一样,都有很多,而我仅以操作 Outlook 日历为例进行学习记录~
对如何使用 Google API 感兴趣的朋友可参考往期文章:如何使用 Google API
学习路径
- 创建 Microsoft 应用;
- 创建 Microsoft 秘钥;
- Token 配置;
- 更新 Authentication 配置;
- 增加/更新接口权限;
- 生成 Access Token;
- 演示如何使用 Microsoft API 操作 Outlook 日历;
- API 调用 typescript 代码演示(简单演示,仅供参考);
1. 创建 Microsoft 应用;
非第一次使用 Microsoft API
- 直接访问:RegisteredApps,点击自己创建的 Microsoft 应用进入操作即可;
第一次使用 Microsoft API
使用 Microsoft 账号(如 Outlook 邮箱账号密码)登录;
-
登录后会看到这样的页面:
登录 https://apps.dev.microsoft.com 点击 “Add an app in the Azure portal” 按钮,进入注册 Microsoft 应用的流程;
a. 进入注册 Microsoft 应用页面;
b. 点击“+ New registration” 按钮;
c. 填写应用名 Name、支持的账号类型 Support account types(不用担心,后续可修改);
d. 点击页面左下角的 Register 按钮进行注册;
e. 几秒钟就能创建出我们的 Microsoft 应用啦~
2. 创建 Microsoft 秘钥;
- 点击上图 Microsoft 应用信息上的“Add a certificate or secret” 入口进入创建秘钥流程;
-
点击“+ New client secret” 按钮创建秘钥;
“+ New client secret” 按钮 填写秘钥描述信息、有效时长;
- 点击左下角的“Add” 按钮创建秘钥;
- 秘钥创建成功:
特别注意:秘钥值只显示一次,而且这个秘钥在后续创建 access token 的时候是必要的,十分重要,我们需要记住;
3. Token 配置;
- 点击左侧的 “Token configuration” 入口,然后点击“+ Add optional claim”按钮开始进行 Token 配置;
- 在打开的右侧弹框中选择 Token type 为 Access,并勾选所有 Claim;
-
点击弹框内的左下角“Add”按钮,
“Add”按钮 勾选 Turn on the Microsoft Graph email...,然后点击“Add”按钮添加 claim;
- claim 添加完成;
4. 更新 Authentication 配置;
- 点击左侧栏的 “Authentication” 入口,修改 Advanced settings 下的 Enable the following mobile and desktop flows 为 Yes;
5. 增加/更新接口权限;
- 点击左侧栏的 “API permissions”,然后点击“+ Add a permission” 按钮;
- 在打开的 Request API permissions 右弹框中,选择 Microsoft Graph 类型的 API;
- 接着选择 Microsoft Graph 类型的 API 的权限类型为 Application permissions,这个至关重要;
- 搜索添加 API 权限;
- 以操作 Outlook 日历为例,需添加以下 API 权限:
- 此时,有些 API 权限的 Admin consent required 值为 Yes,说明需要点击“Grant admin consent for...” 按钮,即管理员授权同意,而这个按钮需要登录的账号有 ADMIN 权限,这个需要特别注意!
- 管理员授权同意;
- 管理员授权同意后:
注:如果未获得管理员授权同意,则后续在调用 Outlook 日历 API 的时候会报权限错误:
{
"error": {
"code": "ErrorAccessDenied",
"message": "Access is denied. Check credentials and try again."
}
}
6. 生成 Access Token;
- 我们可以使用 postman 等工具,或写代码去生成 Access Token,以 postman 工具为例:
其中:
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,并进行测试:
注:登录 https://developer.microsoft.com/en-us/graph/graph-explorer 站点后可用本账号发送真实用户的请求;
- 发送真实用户的请求;
- 然后我们将请求转移到 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"
}
}
}
接下来这个非常重要!!!
这个问题查阅了很多资料,最终在一个网友的答案中找到答案,即,不能用 /me,而应该用 /user/{邮箱地址} 取代,如 url 为:
- 接着我们演示如何使用 Microsoft API 操作 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 也是类似的使用方法,应该不在话下,未来有更多收获我再记录下来~
能力有限,欢迎指正、互相交流,感谢~
如果本文对您有帮助,麻烦点赞、关注!
感谢~