关于c#:WordPress WooCommerce ASP.net API WebHookHandler:WebHook请求必须包含格式为HTML表单数据的实体主体

WordPress WooCommerce ASP.net API WebHookHandler: The WebHook request must contain an entity body formatted as HTML Form Data

我正在尝试为ASP.NET C#中的WordPress WooCommerce创建一个WebHooks WebHookHandler。

我首先创建了一个ASP.NET C#Azure API App WebApplication Project并添加了相关的引用(Microsoft.AspNet.WebHooks.Common,Microsoft.AspNet.WebHooks.Receivers,Microsoft.AspNet.WebHooks.Receivers.WordPress)。添加了WebHookConfig,WordPressWebHookHandler并在GlobalAsax中注册了WebHookConfig。

然后,我将该应用程序发布为Azure App Service。

我的WordPressWebHookHandler仍然是示例的默认值,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
    }
}

在WooCommerce中测试用户创建WebHook时,我可以在日志中看到请求,如下所示。

Webhook request log

但不幸的是,它在调试时从未收到,我看到下面的错误。

Webook request log error

我想也许我需要一个自定义WebHook而不是WordPress特定的一个,因为这是一个WooCommerce Webhook。或者可能在路由中处理错误并最终在另一个控制器中。

任何帮助深表感谢。


您的WebHookReceiver是错误的

期望HTML表单数据不匹配,实际上它应该期待JSON。

WordPressWebHookHandler is still the default

这是导致您的错误的原因。如果查看WordPressWebHookReceiverReceiveAsync()方法实现,则调用ReadAsFormDataAsync()方法,这不是您想要的,因为Content-Typejson。所以,你想做ReadAsJsonAsync()

解决方案:不要使用WordPressWebHookReceiver并将其切换到另一个将调用ReadAsJsonAsync()WordPressWebHookReceiver

看着代码

I am thinking maybe I need a custom WebHook instead of the WordPress specific one as this is a WooCommerce Webhook.

你有正确的想法,所以我挖出一些代码来解释为什么会发生这种情况。

下面的代码块是在WordPressWebHookReceiver中重写的ReceiveAsync()方法。你可以看到它正在调用ReadAsFormDataAsync()而不是你想要的......

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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 provides support for
    receiving WebHooks generated by ASP.NET WebHooks

    Out of the box you can find support for Dropbox, GitHub, MailChimp,
    PayPal, Pusher, Salesforce, Slack, Stripe, Trello, and WordPress but
    it is possible to support any number of other providers

    初始化WebHook接收器

    WebHook Receivers are initialized by registering them, typically in
    the WebApiConfig static class, for example:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            ...

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


    我想对Svek的答案做一些补充,因为我现在已经完成了我的概念验证,并且对接收器有了更多的了解。

    他的回答指出了我正确的方向,但需要一点点补充。

    WordpressWebHookReceiver
    可以接受类型为HttpPost的Wordpress Webhooks。这不适用于Woocommerce,因为Woocommerce发送Json Webhook消息,并且将无法通过构建到WordpressWebHookReceiver类中的HttpPost验证。

    CustomWebHookReceiver
    可以采用自定义ASP.NET Webhooks。自定义ASP.NET webhooks具有特定的验证合作伙伴,包括但不限于"ms-signature"。即使添加标题也是不够的,因为签名也以与开箱即用的Woocommerce不同的方式用于加密消息。基本上,你不能在不改变Wohommerce的Webhook类的情况下将Woocommerce与CustomWebHookReceiver集成。

    GenericWebHookReceiver
    这是你想要的接收器,它基本上接受一组通用的Json数据,并且能够使用"code"查询参数来验证你可以在你的asp.net api应用程序的web.config中添加的秘密。我使用这个接收器来完成概念验证,同时获得了签名验证以及解决蝙蝠工作权利的信息。

    我将开始构建一个真正的解决方案的基本类可以在下面查看,并将JObject更改为我从类调用的方法中的动态对象。如您所见,我目前添加了两种方法,一种用于客户创建,另一种用于订单创建,以调用插入Dynamics 365(以前的CRM)的相应方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    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);
        }
    }


    您在请求中发送的数据格式存在问题。您必须使用HTML表单的格式作为您的错误消息说。

    此处描述了正确的POST数据格式:如何在HTTP POST请求中发送参数?

    如果您的库不这样做,请不要忘记设置Content-Length标头并更正Content-Type。通常内容类型是application/x-www-form-urlencoded