Partager via


Écrire du code dans un connecteur personnalisé

Le code personnalisé transforme les charges utiles des demandes et des réponses au-delà de la portée des modèles de stratégie existants. Lorsque le code est utilisé, il prévaut sur la définition sans code.

Pour plus d’informations, accédez à Créer un connecteur personnalisé à partir de zéro.

Classe de script

Votre code doit implémenter une méthode appelée ExecuteAsync, qui est appelée lors de l’exécution. Vous pouvez créer d’autres méthodes dans cette classe selon vos besoins et les appeler à partir de la méthode ExecuteAsync. Le nom de la classe doit être Script et doit implémenter ScriptBase.

public class Script : ScriptBase
{
    public override Task<HttpResponseMessage> ExecuteAsync()
    {
        // Your code here
    }
}

Définition des classes et interfaces de support

Les classes et interfaces suivantes sont référencées par la classe Script. Ils peuvent être utilisés pour les tests et la compilation locaux.

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);
}

Exemples

Script Hello World

Cet exemple de script renvoie toujours Hello World comme réponse pour toutes les demandes.

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;
}

Script d’expression régulière

L’exemple suivant prend du texte à faire correspondre et l’expression regex et renvoie le résultat de la correspondance dans la réponse.

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;
}

Script de transfert

L’exemple suivant transfère la demande entrante au backend.

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;
}

Script de transfert et de transformation

L’exemple suivant transfère la demande entrante et transforme la réponse renvoyée par le backend.

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;
}

Espaces de noms pris en charge

Tous les espaces de noms C# ne sont pas pris en charge. Actuellement, vous pouvez utiliser les fonctions des espaces de noms suivants uniquement.

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;

Exemples GitHub

Pour des exemples dans le connecteur, accédez à DocuSign Connecteurs Power Platform dans GitHub.

FAQ sur le code personnalisé

Pour en savoir plus sur le code personnalisé, accédez à Étape 4 : (Facultatif) Utiliser la prise en charge du code personnalisé.

Q : Est-il possible d’utiliser plusieurs scripts par connecteur personnalisé ?
R : Non, un seul fichier de script par connecteur personnalisé est pris en charge.

Q : J’obtiens une erreur interne du serveur lors de la mise à jour de mon connecteur personnalisé. Quel pourrait être le problème ?
A : Il s’agit très probablement d’un problème de compilation de votre code. À l’avenir, nous afficherons la liste complète des erreurs de compilation pour améliorer cette expérience. Nous vous recommandons d’utiliser les classes de support pour tester les erreurs de compilation localement pour le moment comme solution de contournement.

Q : Puis-je ajouter la journalisation à mon code et obtenir une trace pour le débogage ?
R : Pas actuellement, mais la prise en charge de cette fonction sera ajoutée à l’avenir.

Q : Comment puis-je tester mon code en attendant ?
A : Testez-le localement et assurez-vous que vous pouvez compiler du code en utilisant uniquement les espaces de noms fournis dans espaces de noms pris en charge. Pour plus d’informations sur les tests locaux, accédez à Écrire du code dans un connecteur personnalisé.

Q : Y a-t-il des limites ?
R : Oui. Votre script doit être exécuté en 2 minutes et la taille de votre fichier de script ne peut pas dépasser 1 Mo. Ce nouveau délai d’attente de 2 minutes s’applique à tous les connecteurs personnalisés nouvellement créés. Pour les connecteurs personnalisés existants, les clients doivent mettre à jour le connecteur pour appliquer le nouveau délai d’expiration.

Q : Puis-je créer mon propre client http dans un code de script ?
R : Actuellement oui, mais nous bloquerons cela à l’avenir. La méthode recommandée est d’utiliser this.Context.SendAsync méthode.

Q : Puis-je utiliser du code personnalisé avec la passerelle de données sur site ?
A: Pas actuellement, non.

Prise en charge du réseau virtuel

Lorsque le connecteur est utilisé dans un Power Platform environnement lié à un réseau virtuel, des limitations s’appliquent :

  • Context.SendAsync utilise un point de terminaison public, il ne peut donc pas accéder aux données des points de terminaison privés exposés sur le réseau virtuel.

Problèmes généraux connus et limitations

L'en-tête OperationId peut être renvoyé au format encodé base64 dans certaines régions. Si la valeur de l'OperationId est requise pour une mise en œuvre, elle doit être décodée en base64 pour être utilisée de manière similaire à ce qui suit.

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
}

Étape suivante

Créer un connecteur personnalisé à partir de zéro

Fournir des commentaires

Nous apprécions grandement les commentaires sur les problèmes liés à notre plate-forme de connecteurs ou les idées de nouvelles fonctionnalités. Pour fournir des commentaires, accédez à Soumettre des problèmes ou obtenir de l’aide avec les connecteurs et sélectionnez votre type de commentaire.