Test1

三、添加 Send an HTTP request to SharePoint

在外层 Apply to each 里面,新建一步:

Send an HTTP request to SharePoint

微软官方说明,这个动作就是让你在 Power Automate 里直接构造并执行 SharePoint REST API 请求,特别适合标准 SharePoint 动作处理不了的需求。

这一步要填的参数

Site Address

填旧 list 所在站点,例如:

https://yourcompany.sharepoint.com/sites/yoursite

Method

填:

GET

因为你现在是读取版本历史,不是更新。

Uri

先用这个版本:

_api/web/lists/GetByTitle('Old Approval List')/items(@{items('Apply_to_each')?['ID']})/versions

这里的含义是:

  • GetByTitle('Old Approval List'):指定旧 list
  • items(ID):指定当前循环这条旧记录
  • /versions:读取该 item 的版本历史

SharePoint REST 支持对 list 和 list item 做 REST 读取;Microsoft Graph 文档也明确支持 list item versions 读取。

Headers

一般加这两个最稳:

Header 1

  • Key:
Accept
  • Value:
application/json;odata=nometadata

Header 2

  • Key:
Content-Type
  • Value:
application/json;odata=nometadata

GET 请求里 Content-Type 不是绝对必须,但这样写通常比较整齐。微软关于 Send an HTTP request to SharePoint 的指导就是按 REST/JSON 请求来用。

Body

留空。
因为 GET 请求不需要 body。


四、先做最小测试

先不要急着解析。
你先加一个:

Compose

内容填:

body('Send_an_HTTP_request_to_SharePoint')

然后跑 1 条测试,看看返回长什么样。

你要重点看:

  • 是否有多个 version
  • 每个 version 里有没有你的 comment 字段
  • 字段名到底叫 Comment 还是别的内部名

五、如果想更精准地取字段,URI 可以升级

如果你已经知道 comment 的内部名就是 Comment,可以试这个更具体的 URI:

_api/web/lists/GetByTitle('Old Approval List')/items(@{items('Apply_to_each')?['ID']})/versions?$select=VersionLabel,Created,Comment

这表示只取:

  • VersionLabel
  • Created
  • Comment

SharePoint REST 支持 OData 风格的 $select

如果字段内部名不是 Comment,就把这里改成真实内部名。


六、如果你还想拿到是谁写的

可以试:

_api/web/lists/GetByTitle('Old Approval List')/items(@{items('Apply_to_each')?['ID']})/versions?$select=VersionLabel,Created,Comment,Editor/Title&$expand=Editor

这里意思是:

  • $expand=Editor
  • 同时 $select=Editor/Title

SharePoint REST 对用户等复杂字段支持 $expand


七、如果返回里看不到 Comment 字段怎么办

那通常有三种可能:

1)内部名不是 Comment

最常见。
你需要先确认内部名。

2)该字段在 versions 里不是直接暴露

这类 append 字段本来就比较特殊,微软也明确提到它在 workflow/output 场景里会出现空白或特殊行为。

3)你需要改用更细的版本接口

可以先取版本列表,再逐个取某个 version 的 fields。Microsoft Graph 文档明确支持:

  • GET /sites/{site-id}/lists/{list-id}/items/{item-id}/versions/{version-id}
    并可展开 fields。

不过在 Power Automate 里,先试 SharePoint REST 的 /versions 通常更直接。


八、你现在最推荐的测试配置

假设:

  • 旧 list 名:Old Approval List
  • 外层循环名:Apply_to_each
  • comment 内部名:Comment

那你这一步可以直接这样填:

Action

Send an HTTP request to SharePoint

Site Address

https://yourcompany.sharepoint.com/sites/yoursite

Method

GET

Uri

_api/web/lists/GetByTitle('Old Approval List')/items(@{items('Apply_to_each')?['ID']})/versions?$select=VersionLabel,Created,Comment

Headers

Accept: application/json;odata=nometadata
Content-Type: application/json;odata=nometadata

Body

留空

然后紧接着加一个:

Compose

body('Send_an_HTTP_request_to_SharePoint')

九、你跑完后应该看什么

在 run history 里打开这一步,重点看:

  • 返回是不是一个数组
  • 是否有多条 version
  • 每条 version 里 Comment 有没有值
  • 这些值是否分别对应历史 comment

如果你看到:

  • 某些 version 的 Comment 有值
  • 而且不同 version 的值不同

那就说明这条路是对的,你后面就可以把它们拼接起来。


十、下一步会是什么

如果这个 HTTP request 成功拿到了多条 version comment,下一步就是:

  1. Parse JSON
  2. Apply to each 遍历 versions
  3. Append to string variableSelect + Join 把 comment 拼成完整文本
  4. Create item 到新 list

concat(‘_api/web/lists/GetByTitle(”你的旧List名称”)/items(‘, string(item()?[‘ID’]), ‘)/versions’)

======================================================

第 2 步:加一个 Parse JSON

在 HTTP 这一步后面,新增:

Parse JSON

Content

填这个动态内容:

body(‘Send_an_HTTP_request_to_SharePoint’)?[‘value’]

因为 /versions 返回的是一个数组,通常就在 value 里。Send an HTTP request to SharePoint 示例里也是先从 body 中取 value 再用于后续循环。

Schema

最简单的方法是:

先跑一次成功测试
复制 HTTP 返回里的 value 数组样本
在 Parse JSON 里点 Generate from sample
把样本粘进去

这样 Power Automate 会自动生成 schema。

第 3 步:初始化一个字符串变量

新增:

Initialize variable

参数这样填:

Name:FullCommentHistory
Type:String
Value:留空

后面用它来累计拼接所有版本的 comment。

第 4 步:加一个新的 Apply to each

这一步是遍历每一个 version。

输入

填:

body(‘Parse_JSON’)

或者直接选 Parse JSON 的输出数组。

因为 Parse JSON 后你拿到的是版本数组,Apply to each 就逐条处理。

第 5 步:在这个 versions 循环里拼接字符串

在内层循环里加:

Append to string variable

Name

选:

FullCommentHistory
Value

建议先用这个版本:

concat(
‘Version: ‘,
coalesce(item()?[‘VersionLabel’], ”),
‘ | Time: ‘,
coalesce(item()?[‘Created’], ”),
‘ | By: ‘,
coalesce(item()?[‘Editor’]?[‘Title’], ”),
decodeUriComponent(‘%0D%0A’),
‘Comment: ‘,
coalesce(item()?[‘Comment’], ”),
decodeUriComponent(‘%0D%0A’),
decodeUriComponent(‘%0D%0A’)
)

这里的作用是把每一个 version 拼成类似:

Version: 3.0 | Time: 2026-01-10T08:00:00Z | By: Alice
Comment: xxxxx

然后空两行,再拼下一条。

concat、coalesce 和 decodeUriComponent(‘%0D%0A’) 这种换行写法,都是 Power Automate/Logic Apps 标准表达式函数。

你可能还要做的一步:排序

/versions 返回的顺序不一定正好就是你最想要的阅读顺序。
所以跑完一次后,你需要看一下:

是从旧到新
还是从新到旧

如果顺序正好反了,你有两个选择:

方案 A

先直接接受当前顺序,后面人工看。

方案 B

在拼接前先做一次数组排序/重组。
这在 Power Automate 里会更复杂一点,所以我建议你先跑一条测试,确认实际顺序再决定。

最后一步:把拼好的内容写到新 list

等 versions 全部循环完后,你的变量 FullCommentHistory 就是完整的 comment 历史文本了。

接下来你在 Create item 或 Update item 里,把目标字段填成:

variables(‘FullCommentHistory’)

不过这里还是要提醒:

即使你把完整历史拼好了,再写回新 list 的原 Comment 列,也通常不会恢复成原来的“逐条 append 历史界面”。
更现实的效果是:把这整段历史作为一块文本写进去,或者作为一条新的追加记录写进去。因为 append-only comment 的旧历史本来就是依赖版本历史来展示的。

给你一版最实用的简化方案

如果你现在只想验证是否成功拿到了完整历史,先做这 3 步就够:

Send an HTTP request to SharePoint
Parse JSON
Apply to each → Append to string variable

等 FullCommentHistory 看起来对了,再决定写回哪里。

你现在可以直接抄的关键表达式
Parse JSON 的 Content
body(‘Send_an_HTTP_request_to_SharePoint’)?[‘value’]
内层 Apply to each 的输入
body(‘Parse_JSON’)
Append to string variable 的 Value
concat(
‘Version: ‘,
coalesce(item()?[‘VersionLabel’], ”),
‘ | Time: ‘,
coalesce(item()?[‘Created’], ”),
‘ | By: ‘,
coalesce(item()?[‘Editor’]?[‘Title’], ”),
decodeUriComponent(‘%0D%0A’),
‘Comment: ‘,
coalesce(item()?[‘Comment’], ”),
decodeUriComponent(‘%0D%0A’),
decodeUriComponent(‘%0D%0A’)
)

======================================================

你现在的前提是:

  • 你已经有一个 Send an HTTP request to SharePoint,并且成功返回了 /versions 的结果
  • 你的 Parse JSON → Content 用的是
    body('Send_an_HTTP_request_to_SharePoint')
  • 返回体里真正要遍历的是 value 数组;Microsoft 的 SharePoint HTTP 指南也明确给了这种模式:当响应里有数组时,循环输入通常用 body('Send_an_HTTP_request_to_SharePoint')['value']

1. Parse JSON

新增动作:Parse JSON

Content

填:

body('Send_an_HTTP_request_to_SharePoint')

Schema

填这一份:

{
"type": "object",
"properties": {
"value": {
"type": "array",
"items": {
"type": "object",
"properties": {
"VersionLabel": {
"type": "string"
},
"Created": {
"type": "string"
},
"Comment": {
"type": "string"
},
"Editor": {
"type": "object",
"properties": {
"Title": {
"type": "string"
}
}
}
}
}
}
}
}

这样写是因为你传给 Parse JSON 的是整个 body,不是 body(...)?['value'];所以 schema 最外层必须是 object,里面再有一个 value 数组。Parse JSON 的作用本来就是把 JSON 解析成后续更容易引用的结构化输出。


2. Initialize variable

新增动作:Initialize variable

Name

FullCommentHistory

Type

String

Value

留空

这是为了后面把每个版本的 comment 历史逐条拼接起来。Data Operations 里的变量、Compose、Select、Join 这类动作本来就是为这类 JSON/数组/字符串处理准备的。


3. Apply to each(遍历 versions)

新增动作:Apply to each

Select an output from previous steps

填:

body('Parse_JSON')?['value']

这是最关键的一步。
Microsoft 的 SharePoint HTTP 指南明确写了:如果响应里是数组,循环输入用 body('Send_an_HTTP_request_to_SharePoint')['value'] 这一类表达式;你现在已经先做了 Parse JSON,所以这里对应改成 body('Parse_JSON')?['value']。循环内部再用 items('Apply_to_each')item() 访问单条元素。


4. Append to string variable(在版本循环里拼接)

在这个 versions 循环里面,新增动作:Append to string variable

Name

FullCommentHistory

Value

填这个表达式:

concat(
'Version: ',
coalesce(item()?['VersionLabel'], ''),
' | Time: ',
coalesce(item()?['Created'], ''),
' | By: ',
coalesce(item()?['Editor']?['Title'], ''),
decodeUriComponent('%0D%0A'),
'Comment: ',
coalesce(item()?['Comment'], ''),
decodeUriComponent('%0D%0A'),
decodeUriComponent('%0D%0A')
)

这里用到的 concatcoalesce 都是标准表达式函数;decodeUriComponent('%0D%0A') 是常见换行写法。Expression functions 文档对这类函数都有定义。

跑出来后的效果大概像这样:

Version: 3.0 | Time: 2026-03-01T10:00:00Z | By: Alice
Comment: some textVersion: 2.0 | Time: 2026-02-01T08:00:00Z | By: Bob
Comment: older text

5. 可选:Compose(先检查拼接结果)

在 versions 循环结束后,你可以先加一个 Compose,确认拼接结果是不是你想要的。

Inputs

variables('FullCommentHistory')

这样你先在运行历史里确认内容是否完整,再决定写回新 list。Compose 本身就是用来构造和检查输出的标准 Data Operation。


6. Create item(写入新 list)

接下来是 Create item
这一步仍然放在你外层“旧 list item”的循环里,也就是每处理完一条旧 item 的 version history,就创建一条新 item。

Site Address

选你的新 list 所在站点

List Name

选你的新 list

各字段怎么填

你原来怎么迁移其他字段,就继续怎么映射。关键是 Comment 这一列:

如果你想把完整历史写进原 Comment

Comment 字段里填:

variables('FullCommentHistory')

如果你后来新建了一个普通文本列,例如 Comment_Migrated

那就在那个字段里填:

variables('FullCommentHistory')

Create item 是 SharePoint 连接器的标准动作,用于在目标 list 创建一条新记录。


7. 重要提醒:写回原 Comment 列会是什么效果

如果你的目标 Comment 列仍然是 Append changes to existing text,那就要有心理预期:

  • 你写进去的这整段历史,通常会作为一条新的值/一条新的追加内容出现
  • 不会自动还原成原来那种逐条历史线程界面

你现在能做的是“把完整历史文本取出来并保存”,但不是“重建 SharePoint 原生 append 历史机制”。这一点和你前面遇到的问题本质一致:append 历史真正依赖的是版本历史,而不是单个当前字段值。你现在这条 /versions 路线,本质上就是在自己重建这段历史文本。


8. 最终的动作顺序

按顺序应该是这样:

  1. Get items(旧 list)
  2. Apply to each(旧 list 的每一条 item)
  3. Send an HTTP request to SharePoint(取当前旧 item 的 versions)
  4. Parse JSON
  5. Initialize variable FullCommentHistory
  6. Apply to each(遍历 body('Parse_JSON')?['value']
  7. Append to string variable
  8. Create item(新 list)
    • Comment = variables('FullCommentHistory')

9. 你可以直接抄的关键表达式汇总

Parse JSON → Content

body('Send_an_HTTP_request_to_SharePoint')

外层版本循环 → 输入

body('Parse_JSON')?['value']

版本循环里拼接 comment 历史

concat(
'Version: ',
coalesce(item()?['VersionLabel'], ''),
' | Time: ',
coalesce(item()?['Created'], ''),
' | By: ',
coalesce(item()?['Editor']?['Title'], ''),
decodeUriComponent('%0D%0A'),
'Comment: ',
coalesce(item()?['Comment'], ''),
decodeUriComponent('%0D%0A'),
decodeUriComponent('%0D%0A')
)

Create item → Comment

variables('FullCommentHistory')

================================

{
“type”: “object”,
“properties”: {
“value”: {
“type”: “array”,
“items”: {
“type”: “object”,
“properties”: {
“VersionLabel”: {
“type”: [“string”, “null”]
},
“Created”: {
“type”: [“string”, “null”]
},
“Comment”: {
“type”: [“string”, “null”]
},
“Editor”: {
“type”: [“object”, “null”],
“properties”: {
“Title”: {
“type”: [“string”, “null”]
}
}
}
}
}
}
}
}

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注