Code in einen benutzerdefinierten Connector schreiben
Benutzerdefinierter Code transformiert Anforderungs- und Antwortnutzlasten über den Umfang vorhandener Richtlinienvorlagen hinaus. Wenn Code verwendet wird, hat er Vorrang vor der codelosen Definition.
Weitere Informationen finden Sie unter Erstellen eines benutzerdefinierten Connectors von Grund auf.
Skriptklasse
Ihr Code muss eine Methode namens ExecuteAsync implementieren, die während der Runtime aufgerufen wird. Sie können nach Bedarf andere Methoden in dieser Klasse erstellen und diese über die ExecuteAsync-Methode aufrufen. Der Klassenname muss Script sein und ScriptBase implementieren.
public class Script : ScriptBase
{
public override Task<HttpResponseMessage> ExecuteAsync()
{
// Your code here
}
}
Definition unterstützender Klassen und Schnittstellen
Auf die folgenden Klassen und Schnittstellen verweist die Scriptklasse. Sie können zum lokalen Testen und Kompilieren verwendet werden.
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);
}
Beispiele
„Hallo Welt“-Skript
Dieses Beispielskript gibt immer „Hallo Welt“ als Antwort für alle Anforderungen zurück.
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;
}
Regex-Skript
Das folgende Beispiel nimmt zu vergleichenden Text und den Regex-Ausdruck und gibt das Ergebnis der Übereinstimmung in der Antwort zurück.
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;
}
Weiterleitungsskript
Das folgende Beispiel leitet die eingehende Anforderung an das Back-End weiter.
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;
}
Weiterleitungs- und Transformierenskript
Das folgende Beispiel leitet die eingehende Anforderung weiter und wandelt die vom Back-End zurückgegebene Antwort um.
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;
}
Unterstützte Namespaces
Nicht alle C#-Namespaces werden unterstützt. Derzeit können Sie nur Funktionen aus den folgenden Namespaces verwenden.
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-Beispiele
Beispiele für den DocuSign Connector finden Sie unter Power Platform Connectors in GitHub.
Häufig gestellte Fragen zu benutzerdefiniertem Code
Weitere Informationen zu benutzerdefiniertem Code finden Sie unter Schritt 4: (Optional) Unterstützung für benutzerdefinierten Code verwenden.
F: Ist es möglich, mehrere Skripte pro benutzerdefiniertem Connector zu verwenden?
A: Nein, es wird nur eine Skriptdatei pro benutzerdefiniertem Connector unterstützt.
F: Beim Aktualisieren meines benutzerdefinierten Connectors wird ein interner Serverfehler angezeigt. Was könnte das Problem sein?
A: Höchstwahrscheinlich liegt ein Problem beim Kompilieren Ihres Codes vor. In Zukunft werden wir die vollständige Liste der Kompilierungsfehler anzeigen, um diese Umgebung zu verbessern. Als Workaround empfehlen wir, die unterstützenden Klassen zu verwenden, um Kompilierungsfehler vorerst lokal zu testen.
F: Kann ich meinem Code eine Protokollierung hinzufügen und eine Ablaufverfolgung zum Debuggen erhalten?
A: Derzeit nicht, aber die Unterstützung hierfür wird in Zukunft hinzugefügt.
F: Wie kann ich meinen Code in der Zwischenzeit testen?
A: Testen Sie es lokal und stellen Sie sicher, dass Sie Code nur mit den in den unterstützten Namespaces bereitgestellten Namespaces kompilieren können. Informationen zum lokalen Testen finden Sie unter Code in einem benutzerdefinierten Connector schreiben.
F: Gibt es irgendwelche Grenzen?
A: Ja. Ihr Skript muss innerhalb von 2 Minuten vollständig ausgeführt werden und die Größe Ihrer Skriptdatei darf 1 MB nicht überschreiten. Dieses neue 2-Minuten-Timeout gilt für alle neu erstellten benutzerdefinierten Konnektoren. Bei vorhandenen benutzerdefinierten Konnektoren müssen Kunden den Konnektor aktualisieren, um das neue Zeitlimit anzuwenden.
F: Kann ich meinen eigenen HTTP-Client im Skriptcode erstellen?
A: Derzeit ja, aber wir werden dies in Zukunft blockieren. Es wird empfohlen, die Methode this.Context.SendAsync zu verwenden.
F: Kann ich benutzerdefinierten Code mit dem lokalen Datengateway verwenden?
A: Derzeit nicht, nein.
Unterstützung von Virtual Network
Wenn der Connector in einer Power Platform Umgebung verwendet wird, die mit einem virtuellen Netzwerk verknüpft ist, gelten Einschränkungen:
- Context.SendAsync verwendet einen öffentlichen Endpunkt und kann daher nicht auf Daten von privaten Endpunkten zugreifen, die in Virtual Network verfügbar sind.
Allgemeine bekannte Probleme und Einschränkungen
Der OperationId-Header wird in bestimmten Regionen möglicherweise in Base64-kodiertem Format zurückgegeben. Wenn der Wert von OperationId für eine Implementierung erforderlich ist, sollte dieser für die Verwendung in folgender Weise oder ähnlich Base64-dekodiert werden.
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
}
Nächster Schritt
Einen benutzerdefinierten Connector von Grund auf neu erstellen
Feedback senden
Wir freuen uns sehr über Feedback zu Problemen mit unserer Connector-Plattform oder neuen Feature-Ideen. Um Feedback abzugeben, gehen Sie zu Probleme übermitteln oder Hilfe zu Konnektoren erhalten und wählen Sie Ihren Feedbacktyp aus.