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 Konnektors ohne Vorlage.
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 lauten Skript und es muss ScriptBase umsetzen.
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 im DocuSign-Connector finden Sie unter Power Platform-Connectors auf GitHub.
Häufig gestellte Fragen zu benutzerdefiniertem Code
Um mehr über benutzerdefinierten Code zu erfahren, gehen Sie zu Schritt 4 (optional): Verwenden Sie den Support für benutzerdefinierten Code.
F: Ist es möglich, mehrere Skripts pro benutzerdefiniertem Konnektor zu verwenden?
A: Nein, es wird nur eine Skriptdatei pro benutzerdefiniertem Konnektor unterstützt.
F: Beim Aktualisieren meines benutzerdefinierten Konnektors erhalte ich einen internen Serverfehler. Was könnte das Problem sein?
A: Höchstwahrscheinlich ist dies ein Problem beim Kompilieren Ihres Codes. In Zukunft werden wir die vollständige Liste der Kompilierungsfehler anzeigen, um diese Umgebung zu verbessern. Wir empfehlen die Verwendung der unterstützenden Klassen, um Kompilierungsfehler zunächst lokal als Problemumgehung zu testen.
F: Kann ich meinem Code Protokollierung hinzufügen und eine Überwachung zum Debuggen erhalten?
A: Derzeit nicht, aber die Unterstützung dafür wird in Zukunft hinzugefügt.
F: Wie kann ich meinen Code bis dahin testen?
A: Testen Sie ihn lokal und stellen Sie sicher, dass Sie Code nur mit den in unterstützte Namescapes bereitgestellten Namespaces kompilieren können. Informationen zu lokalen Testen finden Sie unter Code in einen benutzerdefinierten Konnektor schreiben.
F: Gibt es irgendwelche Grenzwerte?
A: Ja. Die Ausführung Ihres Skripts muss innerhalb von 5 Sekunden abgeschlossen sein und die Größe Ihrer Skriptdatei darf 1 MB nicht überschreiten.
F: Kann ich meinen eigenen Http-Client im Skriptcode erstellen?
A: Derzeit ja, aber wir werden dies in Zukunft blockieren. Die empfohlene Methode ist, die this.Context.SendAsync-Methode zu verwenden.
F: Kann ich benutzerdefinierte Codes mit dem lokalen Datengateway verwenden?
A: Nein, wird derzeit nicht verwendet.
Unterstützung von Virtual Network
Wenn der Connector in einer Power Platform-Umgebung verwendet wird, die mit einem Virtual Network 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
Erstellen Sie einen benutzerdefinierten Konnektor von Grund auf neu
Feedback senden
Wir freuen uns sehr über Feedback zu Problemen mit unserer Connector-Plattform oder neuen Feature-Ideen. Wenn Sie Feedback geben möchten, gehen Sie zu Probleme melden oder Hilfe zu Connectors und wählen Sie einen Feedbacktyp aus.