WordPress WooCommerce ASP.net API WebHookHandler: запрос WebHook должен содержать тело объекта, отформатированное как данные формы HTML

Я пытаюсь создать WebHookHandler для отправки Webhooks из WordPress WooCommerce в ASP.NET С#.

Я начал с создания проекта ASP.NET С# Azure API AppApplication Project и добавления соответствующих ссылок (Microsoft.AspNet.WebHooks.Common, Microsoft.AspNet.WebHooks.Receivers, Microsoft.AspNet.WebHooks.Receivers.WordPress). Добавлен WebHookConfig, WordPressWebHookHandler и зарегистрирован WebHookConfig в GlobalAsax.

Затем я опубликовал приложение в качестве службы приложений Azure.

Мой WordPressWebHookHandler по-прежнему является стандартом примеров и выглядит так:

public class WordPressWebHookHandler : WebHookHandler
{
    public override Task ExecuteAsync(string receiver, WebHookHandlerContext context)
    {
        // make sure we're only processing the intended type of hook
        if("WordPress".Equals(receiver, System.StringComparison.CurrentCultureIgnoreCase))
        {
            // todo: replace this placeholder functionality with your own code
            string action = context.Actions.First();
            JObject incoming = context.GetDataOrDefault<JObject>();
        }

        return Task.FromResult(true);
    }
}

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

Журнал запросов Webhook

Но, к сожалению, он никогда не принимается во время отладки, и я вижу ниже ошибку.

Ошибка журнала запросов Webook

Я думаю, может быть, мне нужен пользовательский WebHook вместо специфичного WordPress, так как это WooCommerce Webhook. Или, возможно, это неправильно в маршрутизации и заканчивается на другом контроллере.

Любая помощь очень ценится.

Ответ 1

Я хотел бы внести некоторые дополнения в ответ Svek, поскольку теперь я получил свою концепцию Proof-of-concept и понял немного больше о приемниках.

Его ответ указал мне в правильном направлении, но нуждается в небольшом дополнении.

WordpressWebHookReceiver Можно взять в Wordpress Webhooks типа HttpPost. Это не работает с Woocommerce, так как Woocommerce отправляет сообщения Json Webhook и отказывает в проверке HttpPost, которая встроена в класс WordpressWebHookReceiver.

CustomWebHookReceiver Может принимать пользовательские веб-узлы ASP.NET. Пользовательские веб-узлы ASP.NET имеют определенного партнера для проверки, который включает, но не ограничивается, "ms-signature". Даже добавления заголовка недостаточно, так как подпись также используется по-разному из-за ошибки Woocommerce для шифрования сообщения. В принципе, вы пришли к выводу, что вы не можете интегрировать Woocommerce с CustomWebHookReceiver, не изменяя классы Woocommerce Webhook.

GenericWebHookReceiver Это получатель, который вы хотите, который принимает в основном общий набор данных Json и сможет использовать параметр запроса "code", чтобы проверить секрет, который вы можете добавить в web.config вашего приложения app.net api. Я использовал этот приемник, чтобы закончить концепцию Proof-of-concept и получил как проверку подписи, так и расшифровку сообщения, действующего справа от летучей мыши.

Мой базовый класс, который я начну строить в реальном решении, можно посмотреть ниже и изменить JObject на динамический объект в методах, которые я вызываю из класса. Как вы можете видеть, у меня есть два метода, которые в настоящее время добавлены: один для клиента создает и один для заказа создает для вызова соответствующие методы, которые вставляют в Dynamics 365 (прежний CRM).

public class GenericJsonWebHookHandler : WebHookHandler
{
    public GenericJsonWebHookHandler()
    {
        this.Receiver = "genericjson";
    }

    public override Task ExecuteAsync(string generator, WebHookHandlerContext context)
    {
        var result = false;

        try
        {
            // Get JSON from WebHook
            var data = context.GetDataOrDefault<JObject>();

            if(context.Id != "crcu" && context.Id != "cror")
                return Task.FromResult(true);

            if (context.Id == "crcu")
            {
                result = WoocommerceCRMIntegrations.Entities.Contact.CreateContactInCRM(data);
            }
            else if (context.Id == "cror")
            {
                result = WoocommerceCRMIntegrations.Entities.Order.CreateOrderInCRM(data);
            }
        }
        catch (Exception ex)
        {
            result = false;
        }


        return Task.FromResult(result);
    }
}

Ответ 2

Ваш WebHookReceiver ошибочен

Существует несоответствие ожидающих данных HTML Form, когда на самом деле это должно ожидать JSON.

WordPressWebHookHandler по-прежнему по умолчанию

Это то, что вызывает вашу ошибку. Если вы посмотрите на реализацию метода WordPressWebHookReceiver, ReceiveAsync(), вызывается метод ReadAsFormDataAsync(), который не, что вы хотите, поскольку ваш Content-Type равен json. Итак, вы хотите делать ReadAsJsonAsync().

Решение: Не используйте WordPressWebHookReceiver и переключите его на другой, который вызовет ReadAsJsonAsync().


Глядя на код

Я думаю, может быть, мне нужен пользовательский WebHook вместо специфичного WordPress, так как это WooCommerce Webhook.

У вас была правильная идея, поэтому я откопал часть кода, чтобы точно объяснить, почему это происходит.

Ниже приведен код ниже метода ReceiveAsync(), который переопределяется в WordPressWebHookReceiver. Вы можете видеть, что он вызывает ReadAsFormDataAsync(), который не то, что вы хотите...

public override async Task<HttpResponseMessage> ReceiveAsync(
    string id, HttpRequestContext context, HttpRequestMessage request)
{
    ...
    if (request.Method == HttpMethod.Post)
    {
        // here is what you don't want to be called
        // you want ReadAsJsonAsync(), In short, USE A DIFFERENT RECEIVER.
        NameValueCollection data = await ReadAsFormDataAsync(request);
        ...
    }
    else
    {
       return CreateBadMethodResponse(request);
    }
}

Быстрый поиск в репозитории для классов, вызывающих метод ReadAsJsonAsync(), показывает, что следующие приемники реализуют его:

  • DynamicsCrmWebHookReceiver
  • ZendeskWebHookReceiver
  • AzureAlertWebHookReceiver
  • KuduWebHookReceiver
  • MyGetWebHookReceiver
  • VstsWebHookReceiver
  • BitbucketWebHookReceiver
  • CustomWebHookReceiver
  • DropboxWebHookReceiver
  • GitHubWebHookReceiver
  • PaypalWebHookReceiver
  • StripeWebHookReceiver
  • PusherWebHookReceiver

Я предположил, что CustomWebHookReceiver будет соответствовать вашим требованиям, поэтому может захватить NuGet здесь. В противном случае вы можете реализовать свое собственное или получить его из этого класса и т.д.


Настройка получателя WebHook

(Скопировано из Документация по Microsoft)

Microsoft.AspNet.WebHooks.Receivers.Custom обеспечивает поддержку получение WebHooks, сгенерированных ASP.NET WebHooks

Из коробки вы можете найти поддержку Dropbox, GitHub, MailChimp, PayPal, Pusher, Salesforce, Slack, Stripe, Trello и WordPress, но можно поддерживать любое количество других поставщиков.

Инициализация приемника WebHook

Ресиверы WebHook инициализируются путем их регистрации, как правило, в статический класс WebApiConfig, например:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        ...

        // Load receivers
        config.InitializeReceiveGitHubWebHooks();
    }
}

Ответ 3

Существует проблема с форматом данных, который вы отправляете в свой запрос. Вы должны использовать формат HTML-формы, как указано в сообщении об ошибке.

Здесь описывается правильный формат данных POST: Как параметры отправляются в HTTP-запрос POST?

Не забудьте установить заголовок Content-Length и исправить Content-Type, если ваша библиотека этого не делает. Обычно тип содержимого application/x-www-form-urlencoded.