在自定义连接器中编写代码
自定义代码转换超出现有策略模板范围的请求和响应有效负载。 使用代码时,它优先于无代码定义。
有关更多信息,请转到 从头开始创建自定义连接器。
脚本类
您的代码需要实现一个名为 ExecuteAsync 的方法,该方法在运行时调用。 您可以根据需要在此类中创建其他方法,然后从 ExecuteAsync 方法调用它们。 类名必须是 Script ,并且必须实现 ScriptBase。
public class Script : ScriptBase
{
public override Task<HttpResponseMessage> ExecuteAsync()
{
// Your code here
}
}
支持类和接口的定义
以下类和接口由 Script 类引用。 它们可用于本地测试和编译。
public abstract class ScriptBase
{
// Context object
public IScriptContext Context { get; }
// CancellationToken for the execution
public CancellationToken CancellationToken { get; }
// Helper: Creates a StringContent object from the serialized JSON
public static StringContent CreateJsonContent(string serializedJson);
// Abstract method for your code
public abstract Task<HttpResponseMessage> ExecuteAsync();
}
public interface IScriptContext
{
// Correlation Id
string CorrelationId { get; }
// Connector Operation Id
string OperationId { get; }
// Incoming request
HttpRequestMessage Request { get; }
// Logger instance
ILogger Logger { get; }
// Used to send an HTTP request
// Use this method to send requests instead of HttpClient.SendAsync
Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken);
}
示例
Hello World 脚本
此示例脚本始终返回 Hello World 作为对所有请求的回复。
public override async Task<HttpResponseMessage> ExecuteAsync()
{
// Create a new response
var response = new HttpResponseMessage();
// Set the content
// Initialize a new JObject and call .ToString() to get the serialized JSON
response.Content = CreateJsonContent(new JObject
{
["greeting"] = "Hello World!",
}.ToString());
return response;
}
正则表达式脚本
以下示例采用一些要匹配的文本和正则表达式,在响应中返回匹配结果。
public override async Task<HttpResponseMessage> ExecuteAsync()
{
// Check if the operation ID matches what is specified in the OpenAPI definition of the connector
if (this.Context.OperationId == "RegexIsMatch")
{
return await this.HandleRegexIsMatchOperation().ConfigureAwait(false);
}
// Handle an invalid operation ID
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
return response;
}
private async Task<HttpResponseMessage> HandleRegexIsMatchOperation()
{
HttpResponseMessage response;
// We assume the body of the incoming request looks like this:
// {
// "textToCheck": "<some text>",
// "regex": "<some regex pattern>"
// }
var contentAsString = await this.Context.Request.Content.ReadAsStringAsync().ConfigureAwait(false);
// Parse as JSON object
var contentAsJson = JObject.Parse(contentAsString);
// Get the value of text to check
var textToCheck = (string)contentAsJson["textToCheck"];
// Create a regex based on the request content
var regexInput = (string)contentAsJson["regex"];
var rx = new Regex(regexInput);
JObject output = new JObject
{
["textToCheck"] = textToCheck,
["isMatch"] = rx.IsMatch(textToCheck),
};
response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = CreateJsonContent(output.ToString());
return response;
}
转发脚本
以下示例将传入请求转发到后端。
public override async Task<HttpResponseMessage> ExecuteAsync()
{
// Check if the operation ID matches what is specified in the OpenAPI definition of the connector
if (this.Context.OperationId == "ForwardAsPostRequest")
{
return await this.HandleForwardOperation().ConfigureAwait(false);
}
// Handle an invalid operation ID
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
return response;
}
private async Task<HttpResponseMessage> HandleForwardOperation()
{
// Example case: If your OpenAPI definition defines the operation as 'GET', but the backend API expects a 'POST',
// use this script to change the HTTP method.
this.Context.Request.Method = HttpMethod.Post;
// Use the context to forward/send an HTTP request
HttpResponseMessage response = await this.Context.SendAsync(this.Context.Request, this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
return response;
}
转发和转换脚本
以下示例转发传入请求并转换从后端返回的响应。
public override async Task<HttpResponseMessage> ExecuteAsync()
{
// Check if the operation ID matches what is specified in the OpenAPI definition of the connector
if (this.Context.OperationId == "ForwardAndTransformRequest")
{
return await this.HandleForwardAndTransformOperation().ConfigureAwait(false);
}
// Handle an invalid operation ID
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.BadRequest);
response.Content = CreateJsonContent($"Unknown operation ID '{this.Context.OperationId}'");
return response;
}
private async Task<HttpResponseMessage> HandleForwardAndTransformOperation()
{
// Use the context to forward/send an HTTP request
HttpResponseMessage response = await this.Context.SendAsync(this.Context.Request, this.CancellationToken).ConfigureAwait(continueOnCapturedContext: false);
// Do the transformation if the response was successful, otherwise return error responses as-is
if (response.IsSuccessStatusCode)
{
var responseString = await response.Content.ReadAsStringAsync().ConfigureAwait(continueOnCapturedContext: false);
// Example case: response string is some JSON object
var result = JObject.Parse(responseString);
// Wrap the original JSON object into a new JSON object with just one key ('wrapped')
var newResult = new JObject
{
["wrapped"] = result,
};
response.Content = CreateJsonContent(newResult.ToString());
}
return response;
}
支持的命名空间
不是所有 C# 命名空间都支持。 目前,您只能使用以下命名空间的函数。
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Xml;
using System.Xml.Linq;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
GitHub 示例
有关连接器中的 DocuSign 示例,请转到 Power Platform GitHub 中的连接器 。
自定义代码常见问题
要了解有关自定义代码的更多信息,请转到 第 4 步:(可选)使用自定义代码支持。
问:是否可以为每个自定义连接器使用多个脚本?
答: 不可以,每个自定义连接器仅支持一个脚本文件。
问:我在更新自定义连接器时收到内部服务器错误。 可能存在什么问题?
A: 很可能是编译代码时出现的问题。 将来,我们会显示编译错误的完整列表以改进此体验。 我们建议暂时使用 支持类 在本地测试编译错误作为解决方法。
问:是否可以将日志记录添加到代码中并获取用于调试的跟踪?
A: 目前没有,但将来会添加对此的支持。
问:在此期间如何测试我的代码?
A: 在本地测试它,并确保您可以仅使用支持的命名空间 中提供的命名空间来编译代码。 有关本地测试的信息,请转到 在自定义连接器中编写代码。
Q: 是否有任何限制?
答:可以。 您的脚本必须在 2 分钟内完成执行,并且脚本文件的大小不能超过 1 MB。 这个新的 2 分钟超时适用于任何新创建的自定义连接器。 对于现有的自定义连接器,客户需要更新连接器以应用新的超时。
问:我是否可以在脚本代码中创建自己的 http 客户端?
A: 目前可以,但我们将来会阻止此功能。 推荐的方法是使用 它。Context.SendAsync 方法。
问:是否可以将自定义代码与本地数据网关一起使用?
A: 目前没有。
虚拟网络支持
在 Power Platform 链接到虚拟网络的环境中使用连接器时,存在限制:
- Context.SendAsync 使用公共终结点,因此它无法访问来自虚拟网络上公开的专用终结点的数据。
常规已知问题和限制
在某些地区中,OperationId 标头可能以 base64 编码格式返回。 如果需要 OperationId 的值进行实施,该值应进行 base64 解码才能通过类似于以下的方式使用。
public override async Task<HttpResponseMessage> ExecuteAsync()
{
string realOperationId = this.Context.OperationId;
// Resolve potential issue with base64 encoding of the OperationId
// Test and decode if it's base64 encoded
try {
byte[] data = Convert.FromBase64String(this.Context.OperationId);
realOperationId = System.Text.Encoding.UTF8.GetString(data);
}
catch (FormatException ex) {}
// Check if the operation ID matches what is specified in the OpenAPI definition of the connector
if (realOperationId == "RegexIsMatch")
// Refer to the original examples above for remaining details
}
下一步
提供反馈
我们非常感谢大家提出有关连接器平台问题或新功能想法的反馈。 若要提供反馈,请转到 提交问题或获取有关连接器 的帮助 ,然后选择您的反馈类型。