DocuSign Connect Webhook с .Net Core 3 - PullRequest
3 голосов
/ 17 октября 2019

Я создаю webhook в .Net Core 3 Web API для DocuSign Connect , чтобы вызывать и предоставлять мне обновления статуса + подписанные документы из конвертов, созданных моим приложением. Пример C # на https://www.docusign.com/blog/dsdev-adding-webhooks-application очень помог мне приблизить меня к цели. Код из примера:

[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook(HttpRequestMessage request)
{
    XmlDocument xmldoc = new XmlDocument();
    xmldoc.Load(request.Content.ReadAsStreamAsync().Result);

    var mgr = new XmlNamespaceManager(xmldoc.NameTable);
    mgr.AddNamespace("a", "http://www.docusign.net/API/3.0");

    XmlNode envelopeStatus = xmldoc.SelectSingleNode("//a:EnvelopeStatus", mgr);
    XmlNode envelopeId = envelopeStatus.SelectSingleNode("//a:EnvelopeID", mgr);
    XmlNode status = envelopeStatus.SelectSingleNode("./a:Status", mgr);

    var targetFileDirectory = @"\\my-network-share\";

    if (envelopeId != null)
    {
        System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{status.InnerText}_.xml", xmldoc.OuterXml);
    }

    if (status.InnerText == "Completed")
    {
        // Loop through the DocumentPDFs element, storing each document.

        XmlNode docs = xmldoc.SelectSingleNode("//a:DocumentPDFs", mgr);
        foreach (XmlNode doc in docs.ChildNodes)
        {
            string documentName = doc.ChildNodes[0].InnerText; // pdf.SelectSingleNode("//a:Name", mgr).InnerText;
            string documentId = doc.ChildNodes[2].InnerText; // pdf.SelectSingleNode("//a:DocumentID", mgr).InnerText;
            string byteStr = doc.ChildNodes[1].InnerText; // pdf.SelectSingleNode("//a:PDFBytes", mgr).InnerText;

            System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{documentId}_{documentName}", byteStr);
        }
    }
}

В целях тестирования мой веб-API разрешает все источники и открытые для внешнего мира через NGROK, и я могу поразить другие конечные точки теста (как GET, так и POST),но по какой-то причине Connect не ударил этот webhook, когда в моем конверте произошло событие, заслуживающее уведомления.

В журналах портала DocuSign Admin я вижу, что Connect вызвал мой webhook, но получил . удаленный сервер возвратил ошибку: (415) Неподдерживаемый тип носителя. . Это привело меня к добавлению атрибута [FromBody] в сигнатуру моего метода, но я все равно получаю ту же ошибку, когда Connectho вызывает мой webhook.

[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook([FromBody] HttpRequestMessage request)
{
    // ... rest of the method was unchanged, removed for brevity
}

Я никогда раньше не использовал HttpRequestMessage, но этовыглядит достаточно просто. В журналах портала DocuSign Admin я заметил, что данные, которые Connect пытался отправить на веб-крючок, представляют собой просто XML. Я мог бы попытаться изменить подпись webhook, чтобы искать XmlDocument вместо HttpRequestMessage, но я не уверен, что, если что-нибудь, мне будет не хватать.

Кто-нибудь еще интегрирован с Connectнедавно через веб-крючок? И смогли ли вы заставить HttpRequestMessage работать на вас?

Добавлено 18.10.2009:

DocuSign упоминает, что тип контента - XML. Вот как выглядит содержимое:

<DocuSignEnvelopeInformation 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns="http://www.docusign.net/API/3.0">
  <EnvelopeStatus>...</EnvelopeStatus>
  <DocumentPDFs>...</DocumentPDFs>
</DocuSignEnvelopeInformation>

Я добавил AddXmlSerializerFormatters() к методу ConfigureServices в Startup.cs. Так как .Net Core 3, мне пришлось настроить его как services.AddControllers().AddXmlSerializerFormatters() вместо services.AddMVC().AddXmlSerializerFormatters() на https://docs.microsoft.com/en-us/aspnet/core/migration/22-to-30?view=aspnetcore-3.0&tabs=visual-studio.

С этим изменением я теперь попытался использовать [FromForm] как и мой webhook IS , но входной параметр request практически пуст ... request.Content = null:

[HttpPost("api/[controller]/ConnectWebHook")]
public void ConnectWebHook([FromForm] HttpRequestMessage request)
{
    // ... rest of the method was unchanged, removed for brevity
}

Поскольку запрос отправляется из DocuSign Connect, я не могу контролироватьзаголовки / формат / контент. Насколько я могу судить, они не представляют объект XML, а не форму, поэтому [FromForm], вероятно, не тот путь.

1 Ответ

1 голос
/ 18 октября 2019

Этот связанный пример не для .net core. HttpRequestMessage больше не является первоклассным гражданином в рамках ядра asp.net и будет рассматриваться как обычная модель.

Просто извлеките содержимое непосредственно из тела запроса, а остальные должны остаться прежними. как в примере.

[HttpPost("api/[controller]/ConnectWebHook")]
public IActionResult ConnectWebHook() {

    Stream stream = Request.Body;

    XmlDocument xmldoc = new XmlDocument();
    xmldoc.Load(stream);

    var mgr = new XmlNamespaceManager(xmldoc.NameTable);
    mgr.AddNamespace("a", "http://www.docusign.net/API/3.0");

    XmlNode envelopeStatus = xmldoc.SelectSingleNode("//a:EnvelopeStatus", mgr);
    XmlNode envelopeId = envelopeStatus.SelectSingleNode("//a:EnvelopeID", mgr);
    XmlNode status = envelopeStatus.SelectSingleNode("./a:Status", mgr);

    var targetFileDirectory = @"\\my-network-share\";

    if (envelopeId != null) {
        System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{status.InnerText}_.xml", xmldoc.OuterXml);
    }

    if (status.InnerText == "Completed") {
        // Loop through the DocumentPDFs element, storing each document.

        XmlNode docs = xmldoc.SelectSingleNode("//a:DocumentPDFs", mgr);
        foreach (XmlNode doc in docs.ChildNodes) {
            string documentName = doc.ChildNodes[0].InnerText; // pdf.SelectSingleNode("//a:Name", mgr).InnerText;
            string documentId = doc.ChildNodes[2].InnerText; // pdf.SelectSingleNode("//a:DocumentID", mgr).InnerText;
            string byteStr = doc.ChildNodes[1].InnerText; // pdf.SelectSingleNode("//a:PDFBytes", mgr).InnerText;

            System.IO.File.WriteAllText($"{targetFileDirectory}{envelopeId.InnerText}_{documentId}_{documentName}", byteStr);
        }
    }

    return Ok();
}
...