通过 SharePoint 工作流调用 Web 服务

可以使用 SharePoint 外接程序模型来创建和部署在外接程序 Web 或主机 Web 上运行的工作流。 这些工作流可以与提供程序托管的外接程序的远程托管部分进行交互。

工作流还可以通过以下两种方式之一调用包含重要业务数据的远程 Web 服务:

  • 通过将查询信息传递到外接程序的远程托管部分。 然后远程 Web 应用程序将调用 Web 服务并将信息传回到 SharePoint。

  • 通过使用 SharePoint Web 代理查询 Web 服务。 工作流将查询结果传递到外接程序的远程托管部分,后者则将信息传递到 SharePoint。

从 Web 服务检索到的信息可以存储在 SharePoint 列表中。

本文介绍了三个代码示例,说明如何从工作流调用 Web 服务,如下表中所列。 在前两个示例中,安装外接程序时,将工作流和列表部署到外接程序 Web。 最后一个示例提供了工作流的基本 shell 以及如何将其部署到主机 Web 并将其与主机 Web 上的列表相关联的说明。

工作流任务和相关示例

任务 示例
从工作流调用自定义 Web 服务。 Workflow.CallCustomService
从工作流调用自定义 Web 服务并使用 SharePoint Web 代理更新 SharePoint。 Workflow.CallServiceUpdateSPViaProxy
将工作流与主机 Web 关联。 Workflow.AssociateToHostWeb

从工作流调用自定义 Web 服务

Workflow.CallCustomService 示例演示如何创建一个工作流,以调用更新 SharePoint 列表数据的自定义 Web 服务。 它还展示了如何设计提供程序托管的外接程序,从而使该外接程序使用与其一起部署的远程托管 Web 应用程序来查询 Web 服务。 当希望与 Web 服务的所有交互都由提供程序托管的外接程序的远程托管部分处理时,此示例非常有用。

此示例通过从远程 Web 应用程序启动一个工作流来运行。 此工作流将用户提交的查询信息提交到远程 Web 应用程序,远程 Web 应用程序将使用该信息构建对 Northwind OData Web 服务的查询。 查询返回指定国家/地区的产品供应商。 收到该信息后,远程 Web 应用程序将更新外接程序部署到外接程序 Web 的产品供应商列表。

注意

Workflow.CallCustomService 示例页介绍了如何部署此加载项。 如果按照博客文章使用 Visual Studio 2013 调试 SharePoint 2013 工作流中的说明操作,还可以在 Visual Studio 中使用 F5 调试进行部署和测试。

Workflow.CallCustomService 示例外接程序起始页包含一个下拉菜单,你可从中选择要为其创建产品供应商列表的国家/地区。

显示示例外接程序的起始页的屏幕截图


屏幕上的“创建”按钮在 Controllers\PartSuppliersController.cs 文件中调用 Create 方法,该方法在外接程序 Web 的“零部件供应商”列表中创建一个新条目。 然后,Create 方法调用在 Services\PartSuppliersService.cs 文件中定义的 Add 方法。 顺序将显示在下面的两个代码示例中。

Create 方法

public ActionResult Create(string country, string spHostUrl)
        {
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
            using (var clientContext = spContext.CreateUserClientContextForSPAppWeb())
            {
                var service = new PartSuppliersService(clientContext);
                var id = service.GetIdByCountry(country);
                if (id == null)
                {
                    id = service.Add(country);
                    TempData["Message"] = "Part Supplier Successfully Created!";
                }
                else
                    TempData["ErrorMessage"] = string.Format("Failed to Create The Part Supplier: There's already a Part Supplier who's country is {0}.", country);

                return RedirectToAction("Details", new { id = id.Value, SPHostUrl = spHostUrl });
            }
        }


Add 方法

public int Add(string country)
        {
            var item = list.AddItem(new ListItemCreationInformation());
            item["Country"] = country;
            item.Update();
            clientContext.ExecuteQuery();
            return item.Id;
        }


创建这个新的列表项之后,外接程序将显示一个可启动审批工作流的“启动工作流”按钮。

显示示例外接程序中的“启动工作流”页的屏幕截图


选择“启动工作流”按钮可触发在 Controllers\PartSuppliersController.cs 文件中定义的 StartWorkflow 此方法将外接程序 Web URL、Web 服务 URL(用于远程托管的 Web 应用程序,而不是 Northwind Web 服务)和上下文标记值打包,并将它们传递给 StartWorkflow 方法。 PartSuppliersService 方法需要与 SharePoint 进行交互的上下文标记。

public ActionResult StartWorkflow(int id, Guid workflowSubscriptionId, string spHostUrl)
        {
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext) as SharePointAcsContext;

            var webServiceUrl = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Data" }, Request.Url.Scheme);
            var payload = new Dictionary<string, object>
                {
                    { "appWebUrl", spContext.SPAppWebUrl.ToString() },
                    { "webServiceUrl", webServiceUrl },
                    { "contextToken",  spContext.ContextToken }
                };

            using (var clientContext = spContext.CreateUserClientContextForSPAppWeb())
            {
                var service = new PartSuppliersService(clientContext);
                service.StartWorkflow(workflowSubscriptionId, id, payload);
            }

            TempData["Message"] = "Workflow Successfully Started!";
            return RedirectToAction("Details", new { id = id, SPHostUrl = spHostUrl });
        }


然后,StartWorkflow 方法将创建一个工作流实例,并将有效负载变量中存储的三个值(appWebUrl、webServiceUrl、contextToken)传递到工作流。

 {
            var workflowServicesManager = new WorkflowServicesManager(clientContext, clientContext.Web);

            var subscriptionService = workflowServicesManager.GetWorkflowSubscriptionService();
            var subscription = subscriptionService.GetSubscription(subscriptionId);

            var instanceService = workflowServicesManager.GetWorkflowInstanceService();
            instanceService.StartWorkflowOnListItem(subscription, itemId, payload);
            clientContext.ExecuteQuery();
        }


工作流启动后,它会向远程托管的 Web 应用程序发出 POST HTTP 请求。 该请求通知 Web 应用程序更新供应商列表,其中列出用户刚刚添加的国家/地区的供应商。 Controllers\DataController.cs 文件包含接收此请求内容的 POST 方法。

public void Post([FromBody]string country)
        {
            var supplierNames = GetSupplierNames(country);
            UpdateSuppliers(country, supplierNames);
        }


GetSupplierNames 方法(位于 Controllers\DataController.cs 文件中)为与所选国家/地区关联的所有供应商构建并执行对 Northwind OData Web 服务的 LINQ 查询。

private string[] GetSupplierNames(string country)
        {
            Uri uri = new Uri("http://services.odata.org/V3/Northwind/Northwind.svc");
            var entities = new NorthwindEntities(uri);
            var names = entities.Suppliers
                .Where(s => s.Country == country)
                .AsEnumerable()
                .Select(s => s.CompanyName)
                .ToArray();
            return names;
        }


然后,UpdateSuppliers 方法将更新新添加的列表项的“供应商”字段。

private void UpdateSuppliers(string country, string[] supplierNames)
        {
            var request = HttpContext.Current.Request;
            var authority = request.Url.Authority;
            var spAppWebUrl = request.Headers["SPAppWebUrl"];
            var contextToken = request.Headers["SPContextToken"];

            using (var clientContext = TokenHelper.GetClientContextWithContextToken(
                spAppWebUrl, contextToken, authority))
            {
                var service = new PartSuppliersService(clientContext);
                service.UpdateSuppliers(country, supplierNames);
            }
        }


在外接程序项目的“审批供应商”目录中查看 workflow.xaml 文件的设计视图时,你将看到(通过在设计视图左下角选择“参数”选项卡)工作流将三个值存储在作为工作流参数向其传递的 payload 变量中。

显示用于输入传递到工作流的负载参数的屏幕的屏幕截图


工作流审批之前发生 HttpSend 活动。 此活动向远程 Web 应用程序发送 POST 查询,用来依次触发对 Northwind Web 服务和列表项更新(包括供应商列表)的调用。 此活动被配置为将请求发送到作为工作流参数传递的 webServiceUrl 值。

显示用于输入 HTTP 发送 Web 服务 URL 的文本框的屏幕截图


POST 请求还会传递在工作流运行所在的列表项中存储的国家/地区值。

显示 HTTP 发送活动的属性网格的屏幕截图


工作流通过请求标头将 appWebUrl 和 contextToken 值发送到 Web 应用程序。 标头还设置发送和接受请求的内容类型。

显示用于添加 HTTP 发送活动请求标头的网格的屏幕截图

如果工作流得到批准,它会将列表项的 isApproved 字段的值更改为 true

从工作流调用自定义 Web 服务并使用 SharePoint Web 代理更新 SharePoint

Workflow.CallServiceUpdateSPViaProxy 示例说明如何设计提供程序托管的外接程序,以查询 Web 服务,然后将该信息通过 SharePoint Web 代理传递到 SharePoint 列表。

该示例显示了一个任务,它在你需要封装与 Web 服务的所有交互以便直接由工作流处理时很有用。 使用 Web 代理可以更轻松地更新远程 Web 应用程序逻辑,无需更新工作流实例。 如果你没有使用代理,并且必须更新 Web 应用程序中的逻辑,则必须删除现有的工作流实例,然后重新部署该外接程序。 出于这个原因,当你需要调用远程 Web 服务时,我们推荐使用这种设计。

注意

Workflow.CallServiceUpdateSPViaProxy 示例页包含有关部署此外接程序的说明。 如果按照博客文章使用 Visual Studio 2013 调试 SharePoint 2013 工作流中的说明操作,还可以在 Visual Studio 中使用 F5 调试来部署和测试加载项。

此示例从远程 Web 应用程序启动工作流。 此工作流将用户提交的查询信息传递到 Northwind OData Web 服务。 查询将返回指定国家或地区的产品供应商。 接收 Web 服务响应后,工作流会将响应中的信息传递到远程 Web 应用程序。 然后,远程 Web 应用程序将更新外接程序已部署到外接程序 Web 的产品供应商列表。

启动 Workflow.CallServiceUpdateSPViaProxy 示例外接程序时,起始页包含一个下拉菜单,你可从中选择要为其创建产品供应商列表的国家/地区。

显示更新了代理工作流外接程序的示例外接程序起始页的屏幕截图


“创建”按钮在 Controllers\PartSuppliersController.cs 文件中调用一个方法,该方法在外接程序 Web 的“零部件供应商”列表中创建一个新条目。 该文件中的 Create 方法调用在 Services\PartSuppliersService.cs 文件中定义的 Add 方法。 这两种方法都显示在下面的两个代码示例中。

Create 方法

public ActionResult Create(string country, string spHostUrl)
        {
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);
            using (var clientContext = spContext.CreateUserClientContextForSPAppWeb())
            {
                var service = new PartSuppliersService(clientContext);
                var id = service.GetIdByCountry(country);
                if (id == null)
                {
                    id = service.Add(country);
                    TempData["Message"] = "Part Supplier Successfully Created!";
                }
                else
                    TempData["ErrorMessage"] = string.Format("Failed to Create The Part Supplier: There's already a Part Supplier who's country is {0}.", country);

                return RedirectToAction("Details", new { id = id.Value, SPHostUrl = spHostUrl });
            }
        }


Add 方法

public int Add(string country)
        {
            var item = list.AddItem(new ListItemCreationInformation());
            item["Country"] = country;
            item.Update();
            clientContext.ExecuteQuery();
            return item.Id;
        }


创建这个新的列表项之后,外接程序将显示一个可启动审批工作流的“启动工作流”按钮。

显示自定义 Web 服务中的“启动工作流”页面的屏幕截图

选择“启动工作流”按钮可触发 Controllers\PartSuppliersController.cs 文件中的 StartWorkflow。 此方法将外接程序 Web URL 和 Web 服务 URL(用于远程托管的 Web 应用程序,而不是 Northwind Web 服务)打包,并将它们传递给 Services\PartSuppliersService.cs 文件中的 StartWorkflow 方法。 工作流通过 Web 代理与远程 Web 应用程序通信,并且 Web 代理添加请求标头中的访问标记。 这就是工作流没有将上下文标记传递给本示例中的 StartWorkflow 方法的原因。 代码显示在以下示例中。

public ActionResult StartWorkflow(int id, Guid workflowSubscriptionId, string spHostUrl)
        {
            var spContext = SharePointContextProvider.Current.GetSharePointContext(HttpContext);

            var webServiceUrl = Url.RouteUrl("DefaultApi", new { httproute = "", controller = "Data" }, Request.Url.Scheme);
            var payload = new Dictionary<string, object>
                {
                    { "appWebUrl", spContext.SPAppWebUrl.ToString() },
                    { "webServiceUrl", webServiceUrl }
                };

            using (var clientContext = spContext.CreateUserClientContextForSPAppWeb())
            {
                var service = new PartSuppliersService(clientContext);
                service.StartWorkflow(workflowSubscriptionId, id, payload);
            }

            TempData["Message"] = "Workflow Successfully Started!";
            return RedirectToAction("Details", new { id = id, SPHostUrl = spHostUrl });
        }


StartWorkflow 方法将创建一个工作流实例,并将有效负载变量中存储的两个值(appWebUrl 和 webServiceUrl)传递到工作流。

public void StartWorkflow(Guid subscriptionId, int itemId, Dictionary<string, object> payload)
        {
            var workflowServicesManager = new WorkflowServicesManager(clientContext, clientContext.Web);

            var subscriptionService = workflowServicesManager.GetWorkflowSubscriptionService();
            var subscription = subscriptionService.GetSubscription(subscriptionId);

            var instanceService = workflowServicesManager.GetWorkflowInstanceService();
            instanceService.StartWorkflowOnListItem(subscription, itemId, payload);
            clientContext.ExecuteQuery();
        }


工作流启动后,工作流在批准之前会对 Northwind Web 服务进行查询,以检索选择的国家/地区的供应商列表。 它通过使用向此端点发送 OData 查询的 HTTPSend 活动执行此操作:"http://services.odata.org/V3/Northwind/Northwind.svc/Suppliers/?$filter=Country eq '" + country.Replace("'", "''") + "'&amp;$select=CompanyName"

应该将 HttpSend 活动配置为具有 Accept 标头的 GET 请求,该标头指定不带元数据的 JSON: application/json;odata=nometadata

显示配置为 GET 请求的 HTTP 发送活动网格的屏幕截图

显示 HTTP 发送活动的“请求标头”网格的屏幕截图


例如,如果用户为新供应商列表项选择了“加拿大”,JSON 格式的响应将如以下示例中所示。

{
    value: [
        {
            CompanyName: "Ma Maison"
        },
        {
            CompanyName: "Forêts d'érables"
        }
    ]
}


工作流启动后,它会通过代理向远程托管的 Web 应用程序发送一个 POST HTTP 请求,其中包含供应商列表。 它通过查询 Web 代理 URL 的 HttpSend 活动完成此操作:appWebUrl + "/_api/SP.WebProxy.invoke"

然后,工作流通过构建和传递自定义服务有效负载来传递它从 Northwind 服务收到的供应商列表。 创建自定义服务有效负载活动属性包含供应商列表和供应商国家/地区 ID。

显示自定义 Web 服务负载活动的属性和值网格的屏幕截图


创建 WebProxy 有效负载活动构造一个有效负载,将此有效负载的内容传递到 Web 代理 URL。

显示“创建 WebProxy 有效负载”活动对话框的屏幕截图


WebProxy 有效负载活动的属性指定了外接程序 Web URL、POST 请求内容长度和类型以及通过请求标头的请求接受类型。

显示 WebProxy 有效负载活动的属性网格的屏幕截图


工作流构造有效负载和请求后,它会使用配置为对 Web 代理 URL 的 POST 请求的 HttpSend 活动将请求传递给 Web 代理。 请求标头在 Content-TypeAccept 标头中指定 JSON 格式的 OData。

显示 HTTP 发送活动的“请求标头”对话框的屏幕截图


Controllers\DataController.cs 文件中的 Post 方法接受工作流通过 Web 代理发送的请求内容。 上一个示例中的 Post 方法调用用于从 Northwind 中检索供应商列表的方法,以及用于更新相应 SharePoint 供应商列表的方法。

由于此示例中的工作流已查询 Northwind 服务,因此该版本的方法只需更新 SharePoint 列表。 它还将外接程序 Web URL 和访问令牌(由 Web 代理传递)传递给 Services\PartSuppliersService.cs 文件中的 UpdateSuppliers 方法,如以下代码示例所示。

public void Post(UpdatePartSupplierModel model)
        {
            var request = HttpContext.Current.Request;
            var authority = request.Url.Authority;
            var spAppWebUrl = request.Headers["SPAppWebUrl"];
            var accessToken = request.Headers["X-SP-AccessToken"];

            using (var clientContext = TokenHelper.GetClientContextWithContextToken(spAppWebUrl, accessToken, authority))
            {
                var service = new PartSuppliersService(clientContext);
                service.UpdateSuppliers(model.Id, model.Suppliers.Select(s => s.CompanyName));
            }
        }


PartSuppliers.cs 文件中的 UpdateSuppliers 方法将更新新创建的列表项的 Suppliers 字段。

public void UpdateSuppliers(int id, IEnumerable<string> supplierNames)
        {
            var item = list.GetItemById(id);
            clientContext.Load(item);
            clientContext.ExecuteQuery();

            string commaSeparatedList = String.Join(",", supplierNames);
            item["Suppliers"] = commaSeparatedList;
            item.Update();
            clientContext.ExecuteQuery();
        }


如果工作流得到批准,它会将列表项的 isApproved 字段的值更改为 true

将工作流与主机 Web 关联

Workflow.AssociateToHostWeb 示例向你展示了如何使用 Visual Studio 中的工具将工作流部署到主机 Web,并将其与主机 Web 上的列表关联。 本示例的说明将向你展示如何在 Visual Studio 中创建工作流、将其部署到主机 Web 并将其与主机 Web 上的列表关联。

该示例中包含可与任何列表关联的简单工作流。 部署此工作流的说明介绍了如何通过打包外接程序、打开应用程序包并编辑配置文件,然后手动将其重新打包再部署到主机 Web 中,来绕开 Visual Studio 工作流工具的当前限制。

在 Visual Studio 中打开此项目时,你将看到这是一个简单的通用工作流,设计用于任何 SharePoint 列表。 除了工作流任务列表外,它不会部署任何可以关联的列表。

注意

无法使用 Visual Studio 2013 执行此示例中介绍的任务。 此示例提供了实用的解决方法。 如果 Visual Studio 工具今后发生更新,可能就不需要使用此解决方法。

将工作流部署到主机 Web

  1. 在项目资源管理器中打开 Workflow.AssociateToHostWeb 外接程序项目的快捷菜单(右键单击),然后选择“发布”。 你会看到一个包含“打包应用程序”按钮的窗口。

    显示用于发布示例外接程序的“发布应用程序”页面的屏幕截图

  2. 选择“ 打包应用”时,Visual Studio 会在解决方案的目录中创建 Workflow.AssociateToHostWeb.app 文件 bin\Debug\app.publish\1.0.0.0 。 此 .app 文件类型为 zip 文件。

  3. 首先将文件扩展名更改为 .zip,提取文件的内容。

  4. 在已提取的目录中,找到并打开名为 WorkflowManifest.xml 的 XML 文件。 该文件为空。

  5. 向文件中添加以下 XML 片段,然后保存文件。

       <SPIntegratedWorkflow xmlns="http://schemas.microsoft.com/sharepoint/2014/app/integratedworkflow">
         <IntegratedApp>true</IntegratedApp>
       </SPIntegratedWorkflow>
    
  6. 选择提取的文件夹中的所有文件,然后打开文件的快捷菜单(右键单击),并选择“发送到”>“压缩 (zip) 文件夹”。

  7. 在刚刚创建的 zip 文件上,将文件扩展名更改为 .app。 您现在应该具有新的 Workflow.AssociateToHostWeb.app 文件包,其中包含更新的 WorkflowManifest.xml 文件。

  8. 将外接程序添加到应用程序目录。

  9. 将外接程序安装到主机网站。

  10. 转到主持网站上的列表,然后选择页面左上角的“列表”编辑选项。 你会看到“工作流设置”下拉菜单。

    显示列表工作流设置的屏幕截图

  11. 从下拉菜单中选择“添加工作流”。

  12. 你现在可以看到类似于下图中的图像的选项。 从可用选项列表中选择“Workflow.AssociateToHostWeb”外接程序。

    显示“添加工作流”设置页面的屏幕截图

现在,你已将工作流部署到主机 Web 并将其与主机 Web 上的列表相关联。 你可以手动触发工作流,也可以在 Visual Studio 中更新工作流使其以其他方式触发。

另请参阅