使用 .Net WebHooks 作为 RESThooks 连接到 Zapier
Hooking to Zapier using .Net WebHooks as RESThooks
我正在考虑创建一个 "Zap App" 并且想知道是否有人使用新的 .Net Webhooks 这样做过。他们似乎有RESTHooks要求的"pattern",Subcription/Publish机制。它工作的例子不多,我想在花了几天时间实施它并发现它不兼容之前检查一下。
挂钩到 Zapier 的实际代码示例会很棒!
进行了一些研究,但我终于让 Zapier Rest Hooks 工作了。不像我希望的那样直截了当(更有可能是我的理解有点慢)。客户支持非常友好,所以请不要犹豫,将您的问题通过电子邮件发送给他们。此外,一旦你开始使用它,它就会非常强大,尽管它们的普通 webhook 机制也可以工作,并且不需要你创建 Zap 应用程序。在撰写本文时,我还没有推出该应用程序,尽管它在本地运行。这假设您已经开始在开发人员仪表板中创建自己的 Zapier 应用程序。这很简单,所以我不会在这里介绍。
此说明将仅涵盖创建单个触发器(尽管您创建了另外 1 个私有触发器来支持用户身份验证,而我创建了另一个用于动态下拉目的)并且仅作为具有基本身份验证的 RESThook。基础知识是:
1. 创建 webhooks 以允许 Zapier 添加、更新和删除对你的行动。通过 "subscribing" Zapier 不必轮询您的 webhook,而是当订阅的操作发生在您这边时,您响应 Zapier 在订阅过程中给您的 URL。
2. 创建一个数据库 table 记录这些订阅,存储您需要的数据,然后 Post 响应返回到 Zapier 提供的 URL 当您触发操作时。
3.动作触发时,识别和post数据 你告诉 zapier 你会发送。 Zapier 非常聪明,可以为您映射数据(JSON 或 XML),因此当连接到另一个应用程序时,用户可以在两者之间进行映射。
还有一些细节。这是在 .Net 上用 C# 完成的,但我认为这些概念在任何其他语言或平台上也应该同样适用。
首先是 RESTHook。以下是 RESTHook 方法的示例。请注意,我花了几天时间试图弄清楚 Zapier 脚本方面,所以我对命名约定并不完全满意,但希望你能理解。
在这种情况下,有一个 "form" 的概念,它是 JSON 的一大块,包含我关心的数据,用户和该用户所属的帐户。他们在系统中都有一个唯一的 ID。最后,订阅本身有一个 id。当您订阅时,特定帐户中的特定用户正在订阅特定的表格,当特定的触发器(该表格的提交)完成时,该表格将被发送到 Zapier。
RESTHooks:
首先是 RouteConfig
,它将路径映射到要执行的方法。你可以看到我实现的所有方法。其中一些未使用,只是为了将来可能使用(如更新订阅)而包含在内。
// ZAPIER Webhooks
routes.MapRoute(
"User_Form_List",
"api/zapier/user/formlist",
new { controller = "Zapier", action = "RetrieveFormListForUser" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Authenticate_Subscription",
"api/zapier/authenticate",
new { controller = "Zapier", action = "WebhookAuthenticate" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Test_Subscription",
"api/zapier/subscription/testdata",
new { controller = "Zapier", action = "TestData" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Create_Submission",
"api/zapier/subscription/create",
new { controller = "Zapier", action = "CreateSubscription" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"List_Subscriptions",
"api/zapier/subscription",
new { controller = "Zapier", action = "ListSubscriptions" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Get_Subscriptions",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "GetSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Update_Subscription",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "UpdateSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("PUT") }
);
routes.MapRoute(
"Delete_Subscription",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "DeleteSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("DELETE") }
);
对应的代码是(为了减少代码量我也把错误处理去掉了):
public class ZapierController : BaseController //(this inherits from Controller)
{
private readonly IMyRepository _DBrepository;
public ZapierController(IMyRepository repository, ...lots of other autowiring you don't need or care about)
: base(logger)
{
_DBrepository = repository;
}
#region Zapier Subscriptions
// api/zapier/subscription/create : Creates a subscription
[HttpGet]
public ActionResult CreateSubscription()
{
ApiResult authresult = Authenticate();
if (authresult.code != 201)
{
return JsonApiResult(authresult);
}
// Get the request parameters
var reqParams = GetParameters();
// Create the subscription so long as it does not already exist
WebhookSubscription sub = new WebhookSubscription();
// _currentUser and _currentAccount are set as part of the authenticate and stored in our base controller
sub.AccountId = _currentAccount.Id;
sub.UserId = _currentUser.UserId;
sub.TargetURL = reqParams["target_url"];
sub.EventType = reqParams["target_event"];
sub.FormId = Int32.Parse(reqParams["form_id"]);
sub.IsActive = true;
ObjectResult workflowActionRecord = _DBrepository.createWebhookSubscription(sub);
sub.Id = workflowActionRecord.objectId;
// return the subscription back to Zapier in the result. Zapier will remember it
var result = new ApiResult();
result.data.id = workflowActionRecord.objectId;
result.data.subscription = sub;
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/authenticate : used to test authentication
[HttpGet]
public ActionResult WebhookAuthenticate()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/user/formlist : returns list of forms for this user
[HttpGet]
public ActionResult RetrieveFormListForUser()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
List<Form> forms = _DBRepository.FormListRetrieveByUser(_currentUser, false);
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
serializer.NullValueHandling = NullValueHandling.Ignore;
// Again Zapier likes arrays returned
JArray objarray = JArray.FromObject(forms);
return JsonApiResultDynamic(objarray);
}
// api/zapier/subscription/testdata : returns test data for zapier
[HttpGet]
public ActionResult TestData()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
serializer.NullValueHandling = NullValueHandling.Ignore;
// Get the request parameters
var reqParams = GetParameters();
int chosenFormId = -1;
// We need the form Id to proceed
if (reqParams != null && reqParams["form_id"] != null)
chosenFormId = Int32.Parse(reqParams["form_id"]);
else
return JsonApiResult(new ApiResult() { code = 403, error = "Form Id Not Found" });
// Get the form by Form Id, and return the JSON...I have removed that code, but make sure the result is place in an Array
var resultdata = new[] { myFinalFormJSON };
JArray objarray = JArray.FromObject(resultdata);
return JsonApiResultDynamic(objarray);
}
// api/zapier/subscription : returns list of subscriptions by account
[HttpGet]
public ActionResult ListSubscriptions()
{
ApiResult authresult = Authenticate();
// Get a list all subscriptions for the account
List<WebhookSubscription> actionData = _DBrepository.accountWebhookSubscriptions(_currentAccount.Id);
var result = new ApiResult();
result.code = 201;
result.data.subscriptions = actionData;
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : Creates a subscription
[HttpGet]
public ActionResult GetSubscription(int id)
{
ApiResult authresult = Authenticate();
// Get a list all subscriptions for the account
WebhookSubscription actionData = _DBrepository.getWebhookSubscription(id);
var result = new ApiResult();
result.data.subscription = actionData; ;
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : updates a subscription
[HttpPut]
public ActionResult UpdateSubscription(int id)
{
ApiResult authresult = Authenticate();
// get target url and eventy type from the body of request
string jsonString = RequestBody();
var json = CommonUtils.DecodeJson(jsonString);
// Create the subscription so long as it does not already exist
WebhookSubscription sub = _DBrepository.getWebhookSubscription(id);
var result = new ApiResult();
if (sub != null)
{
sub.TargetURL = json.target_url; ;
sub.EventType = json.eventType;
ObjectResult objResult = _DBrepository.updateWebhookSubscription(sub);
result.code = 201;
}
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : deletes a subscription
[HttpDelete]
public ActionResult DeleteSubscription(int id)
{
ApiResult authresult = Authenticate();
// Delete a subscription
_DBrepository.deleteWebhookSubscription(id);
var result = new ApiResult();
result.code = 201;
return JsonApiResult(result);
}
// We need to Basic Auth for each call to subscription
public ApiResult Authenticate()
{
// get auth from basic authentication header
var auth = this.BasicAuthHeaderValue();
// parse credentials from auth
var userCredentials = Encoding.UTF8.GetString(Convert.FromBase64String(auth));
var parts = CommonUtils.SplitOnFirst(userCredentials, ":");
var username = parts[0];
var password = parts[1];
// authenticate user against repository
if (!_DBrepository.UserAuthenticate(username, password))
{
_logger.Info("Invalid Authentication: " + username);
return new ApiResult() { code = 401, error = "invalid authentication" };
}
return new ApiResult() { code = 201, error = "successful authentication" };
}
}
将保存订阅的数据库 table 如下所示。我将省略阅读和写作方面,因为您可能有不同的机制。
Create.Table("WebhookSubscription")
.WithColumn("Id").AsInt32().Identity().PrimaryKey().NotNullable()
.WithColumn("AccountId").AsInt32().NotNullable()
.WithColumn("UserId").AsInt32().NotNullable()
.WithColumn("EventType").AsString(256).NotNullable()
.WithColumn("TargetURL").AsString(1000).NotNullable()
.WithColumn("IsActive").AsBoolean().NotNullable()
.WithColumn("CreatedOn").AsDateTime().Nullable()
.WithColumn("FormId").AsInt32().NotNullable().WithDefaultValue(0);
.WithColumn("UpdatedAt").AsDateTime().Nullable();
为了清楚起见,以下列的 meaning/use:
- Id - 唯一订阅 ID。将用于退订
- AccountId - 用户订阅的帐户 ID。如果您希望所有这些都在帐户级别上工作,可以改为这样做
- UserId - 订阅用户的 Id
- EventType - 您的 Action 要响应的事件类型,例如 "new_form_submission"
- TargetURL - 订阅时zapier给你的目标URL。将在您 post 您的 JSON 发起操作时
- FormId - 提交时用户想要操作的表单 ID
这就是订阅所需的代码(显然你不能把它丢在那里让它工作 - 省去了很多钱 space)...
触发码
只剩下代码是实际的触发器代码 - 当您的代码中遇到要查找的事件时执行的代码。因此,例如,当用户提交 "form" 时,我们希望将该表单 JSON 发送到 Zapier。现在我们已经设置了所有其他代码,这部分非常简单。首先,检测我们有一个需要 Zapier 响应的提交到达的代码:
查看表单提交是否 registered/subscribed 到 Zapier 的实际代码:
public BusinessResult FormSubmitted(string jsonString)
{
var json = CommonUtils.DecodeJson(jsonString);
var account = _DBrepository.AccountRetrieveById(_currentUser.AccountId.Value); // Assumes user has bee authenticated
// inject additional meta data into json and retrieve submission/alert settings
var form = _DBformRepository.FormRetrieveById((int)json.formId);
// Lookup Subscription Webhooks
List<WebhookSubscription> subscriptions = _DBrepository.accountWebhookSubscriptions(account.Id);
if (subscriptions != null && subscriptions.Count > 0)
{
foreach (WebhookSubscription sub in subscriptions)
{
if (sub.EventType.Equals("new_form_submission") && sub.FormId == form.Id)
{
_webhookService.NewFormSubmission(sub, jsonString, form.Name, account.Name, account.Id);
}
}
}
}
最后是 post 的代码,将响应返回给 Zapier,Zapier 将解析 JSON 并将其发送给适当的各方:
public class WebhookService : IWebhookService
{
protected readonly IRepository _DBrepository;
public WebhookService(IRepository repository)
{
_DBrepository = repository;
}
public void NewFormSubmission(string formResultJSON)
{
throw new NotImplementedException();
}
public void NewFormSubmission(WebhookSubscription subscription, string formResultJSON, string formName, string accountName, int accountId)
{
// Now post to webhook URL
string response;
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
// Needs to be an array sent to Zapier
response = client.UploadString(subscription.TargetURL, "POST", "[" + formResultJSON + "]");
}
}
}
好的,这应该可以帮助您完成大部分工作。但是将 code/webhooks 连接到 Zapier 是变得有点困难的地方。现在的想法是使用 Development Dashboard 将上面的代码连接到您的 Zapier 应用程序中。您必须开始创建 Zapier 应用程序。您需要 2 个主要触发器 - 您尝试实施的基本操作(在本例中为 "New Form Submission")和身份验证,以便 Zapier 可以在用户创建 Zap 时对用户进行身份验证(在本例中为 "Test Auth" ).我使用的是 Basic Auth,但其他的也受支持(OAuth 等)。此外,我添加了一个触发器,它将 return 用户有权访问的表单列表。由于不是必需的,我不会在屏幕截图中显示该实现:
我不会显示 "Test Auth" 的布线,因为它进行得非常顺利(如果有人需要,我会添加它——天知道是否有人会读到这篇文章)。所以这里是 "New Form Submission":
的接线,一页一页
第 1 页
第 2 页
这是我连接表单列表的地方,它提供了一个表单列表,用户可以从中创建 Zap select。除非您有要显示的动态数据,否则您可能可以跳过它(将其留空)。为了完整起见,我将其包括在内:
第 3 页
这里连接测试数据
第 4 页
此页面允许您输入示例数据。跳过这个,因为它非常简单。
编写脚本API
现在您已经连接了您的第一个 Zap 触发器!但是等等,我们还没有完成。为了使订阅过程正常工作,我们需要添加一些脚本。这是整个过程中最难的部分,而且不是很直观。所以在原来的主屏幕上,向下一点你会看到 Scripting API:
现在您必须拥有用于 RESTHook 订阅的脚本。我不打算详细介绍,因为 Zapier 确实有这方面的文档,但很高兴知道 Zapier 确实将数据存储为订阅的一部分。在此之后我们还需要再做一个接线步骤...
var Zap = {
pre_subscribe: function(bundle) {
bundle.request.method = 'GET';
bundle.request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
bundle.request.params = {
target_url: bundle.subscription_url,
target_event:bundle.event,
form_id:bundle.trigger_fields.form_id
};
bundle.request.data = $.param({
target_url: bundle.subscription_url,
target_event:bundle.event,
form_id:bundle.trigger_fields.form_id
});
return bundle.request;
},
post_subscribe: function(bundle) {
// must return a json serializable object for use in pre_unsubscribe
var data = JSON.parse(bundle.response.content);
// we need this in order to build the {{webhook_id}}
// in the rest hook unsubscribe url
return {webhook_id: data.id};
},
pre_unsubscribe: function(bundle) {
bundle.request.method = 'DELETE';
bundle.request.data = null;
return bundle.request;
},
new_form_submission_pre_poll: function(bundle) {
bundle.request.method = 'GET';
bundle.request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
bundle.request.params = bundle.trigger_fields;
bundle.request.data = $.param({
form_id:bundle.trigger_fields.form_id
});
return bundle.request;
}
};
这有点道理...但是看看 Zapier 文档,它应该会有所帮助。或者 post 这里的问题,我会尽力回答...这比我预期的要大!
管理触发器设置
最后,我们需要完成订阅的连接...
然后我们设置我们之前创建的 RESTHook 方法:
就是这样。希望这会节省一些时间和学习经验!
我正在考虑创建一个 "Zap App" 并且想知道是否有人使用新的 .Net Webhooks 这样做过。他们似乎有RESTHooks要求的"pattern",Subcription/Publish机制。它工作的例子不多,我想在花了几天时间实施它并发现它不兼容之前检查一下。
挂钩到 Zapier 的实际代码示例会很棒!
进行了一些研究,但我终于让 Zapier Rest Hooks 工作了。不像我希望的那样直截了当(更有可能是我的理解有点慢)。客户支持非常友好,所以请不要犹豫,将您的问题通过电子邮件发送给他们。此外,一旦你开始使用它,它就会非常强大,尽管它们的普通 webhook 机制也可以工作,并且不需要你创建 Zap 应用程序。在撰写本文时,我还没有推出该应用程序,尽管它在本地运行。这假设您已经开始在开发人员仪表板中创建自己的 Zapier 应用程序。这很简单,所以我不会在这里介绍。
此说明将仅涵盖创建单个触发器(尽管您创建了另外 1 个私有触发器来支持用户身份验证,而我创建了另一个用于动态下拉目的)并且仅作为具有基本身份验证的 RESThook。基础知识是:
1. 创建 webhooks 以允许 Zapier 添加、更新和删除对你的行动。通过 "subscribing" Zapier 不必轮询您的 webhook,而是当订阅的操作发生在您这边时,您响应 Zapier 在订阅过程中给您的 URL。
2. 创建一个数据库 table 记录这些订阅,存储您需要的数据,然后 Post 响应返回到 Zapier 提供的 URL 当您触发操作时。
3.动作触发时,识别和post数据 你告诉 zapier 你会发送。 Zapier 非常聪明,可以为您映射数据(JSON 或 XML),因此当连接到另一个应用程序时,用户可以在两者之间进行映射。
还有一些细节。这是在 .Net 上用 C# 完成的,但我认为这些概念在任何其他语言或平台上也应该同样适用。
首先是 RESTHook。以下是 RESTHook 方法的示例。请注意,我花了几天时间试图弄清楚 Zapier 脚本方面,所以我对命名约定并不完全满意,但希望你能理解。
在这种情况下,有一个 "form" 的概念,它是 JSON 的一大块,包含我关心的数据,用户和该用户所属的帐户。他们在系统中都有一个唯一的 ID。最后,订阅本身有一个 id。当您订阅时,特定帐户中的特定用户正在订阅特定的表格,当特定的触发器(该表格的提交)完成时,该表格将被发送到 Zapier。
RESTHooks:
首先是 RouteConfig
,它将路径映射到要执行的方法。你可以看到我实现的所有方法。其中一些未使用,只是为了将来可能使用(如更新订阅)而包含在内。
// ZAPIER Webhooks
routes.MapRoute(
"User_Form_List",
"api/zapier/user/formlist",
new { controller = "Zapier", action = "RetrieveFormListForUser" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Authenticate_Subscription",
"api/zapier/authenticate",
new { controller = "Zapier", action = "WebhookAuthenticate" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Test_Subscription",
"api/zapier/subscription/testdata",
new { controller = "Zapier", action = "TestData" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Create_Submission",
"api/zapier/subscription/create",
new { controller = "Zapier", action = "CreateSubscription" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"List_Subscriptions",
"api/zapier/subscription",
new { controller = "Zapier", action = "ListSubscriptions" },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Get_Subscriptions",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "GetSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("GET") }
);
routes.MapRoute(
"Update_Subscription",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "UpdateSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("PUT") }
);
routes.MapRoute(
"Delete_Subscription",
"api/zapier/subscription/{id}",
new { controller = "Zapier", action = "DeleteSubscription", id = 0 },
new { httpMethod = new HttpMethodConstraint("DELETE") }
);
对应的代码是(为了减少代码量我也把错误处理去掉了):
public class ZapierController : BaseController //(this inherits from Controller)
{
private readonly IMyRepository _DBrepository;
public ZapierController(IMyRepository repository, ...lots of other autowiring you don't need or care about)
: base(logger)
{
_DBrepository = repository;
}
#region Zapier Subscriptions
// api/zapier/subscription/create : Creates a subscription
[HttpGet]
public ActionResult CreateSubscription()
{
ApiResult authresult = Authenticate();
if (authresult.code != 201)
{
return JsonApiResult(authresult);
}
// Get the request parameters
var reqParams = GetParameters();
// Create the subscription so long as it does not already exist
WebhookSubscription sub = new WebhookSubscription();
// _currentUser and _currentAccount are set as part of the authenticate and stored in our base controller
sub.AccountId = _currentAccount.Id;
sub.UserId = _currentUser.UserId;
sub.TargetURL = reqParams["target_url"];
sub.EventType = reqParams["target_event"];
sub.FormId = Int32.Parse(reqParams["form_id"]);
sub.IsActive = true;
ObjectResult workflowActionRecord = _DBrepository.createWebhookSubscription(sub);
sub.Id = workflowActionRecord.objectId;
// return the subscription back to Zapier in the result. Zapier will remember it
var result = new ApiResult();
result.data.id = workflowActionRecord.objectId;
result.data.subscription = sub;
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/authenticate : used to test authentication
[HttpGet]
public ActionResult WebhookAuthenticate()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/user/formlist : returns list of forms for this user
[HttpGet]
public ActionResult RetrieveFormListForUser()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
List<Form> forms = _DBRepository.FormListRetrieveByUser(_currentUser, false);
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
serializer.NullValueHandling = NullValueHandling.Ignore;
// Again Zapier likes arrays returned
JArray objarray = JArray.FromObject(forms);
return JsonApiResultDynamic(objarray);
}
// api/zapier/subscription/testdata : returns test data for zapier
[HttpGet]
public ActionResult TestData()
{
ApiResult authresult = Authenticate();
var result = new ApiResult();
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new JavaScriptDateTimeConverter());
serializer.NullValueHandling = NullValueHandling.Ignore;
// Get the request parameters
var reqParams = GetParameters();
int chosenFormId = -1;
// We need the form Id to proceed
if (reqParams != null && reqParams["form_id"] != null)
chosenFormId = Int32.Parse(reqParams["form_id"]);
else
return JsonApiResult(new ApiResult() { code = 403, error = "Form Id Not Found" });
// Get the form by Form Id, and return the JSON...I have removed that code, but make sure the result is place in an Array
var resultdata = new[] { myFinalFormJSON };
JArray objarray = JArray.FromObject(resultdata);
return JsonApiResultDynamic(objarray);
}
// api/zapier/subscription : returns list of subscriptions by account
[HttpGet]
public ActionResult ListSubscriptions()
{
ApiResult authresult = Authenticate();
// Get a list all subscriptions for the account
List<WebhookSubscription> actionData = _DBrepository.accountWebhookSubscriptions(_currentAccount.Id);
var result = new ApiResult();
result.code = 201;
result.data.subscriptions = actionData;
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : Creates a subscription
[HttpGet]
public ActionResult GetSubscription(int id)
{
ApiResult authresult = Authenticate();
// Get a list all subscriptions for the account
WebhookSubscription actionData = _DBrepository.getWebhookSubscription(id);
var result = new ApiResult();
result.data.subscription = actionData; ;
result.code = 201;
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : updates a subscription
[HttpPut]
public ActionResult UpdateSubscription(int id)
{
ApiResult authresult = Authenticate();
// get target url and eventy type from the body of request
string jsonString = RequestBody();
var json = CommonUtils.DecodeJson(jsonString);
// Create the subscription so long as it does not already exist
WebhookSubscription sub = _DBrepository.getWebhookSubscription(id);
var result = new ApiResult();
if (sub != null)
{
sub.TargetURL = json.target_url; ;
sub.EventType = json.eventType;
ObjectResult objResult = _DBrepository.updateWebhookSubscription(sub);
result.code = 201;
}
return JsonApiResult(result);
}
// api/zapier/subscription/{id} : deletes a subscription
[HttpDelete]
public ActionResult DeleteSubscription(int id)
{
ApiResult authresult = Authenticate();
// Delete a subscription
_DBrepository.deleteWebhookSubscription(id);
var result = new ApiResult();
result.code = 201;
return JsonApiResult(result);
}
// We need to Basic Auth for each call to subscription
public ApiResult Authenticate()
{
// get auth from basic authentication header
var auth = this.BasicAuthHeaderValue();
// parse credentials from auth
var userCredentials = Encoding.UTF8.GetString(Convert.FromBase64String(auth));
var parts = CommonUtils.SplitOnFirst(userCredentials, ":");
var username = parts[0];
var password = parts[1];
// authenticate user against repository
if (!_DBrepository.UserAuthenticate(username, password))
{
_logger.Info("Invalid Authentication: " + username);
return new ApiResult() { code = 401, error = "invalid authentication" };
}
return new ApiResult() { code = 201, error = "successful authentication" };
}
}
将保存订阅的数据库 table 如下所示。我将省略阅读和写作方面,因为您可能有不同的机制。
Create.Table("WebhookSubscription")
.WithColumn("Id").AsInt32().Identity().PrimaryKey().NotNullable()
.WithColumn("AccountId").AsInt32().NotNullable()
.WithColumn("UserId").AsInt32().NotNullable()
.WithColumn("EventType").AsString(256).NotNullable()
.WithColumn("TargetURL").AsString(1000).NotNullable()
.WithColumn("IsActive").AsBoolean().NotNullable()
.WithColumn("CreatedOn").AsDateTime().Nullable()
.WithColumn("FormId").AsInt32().NotNullable().WithDefaultValue(0);
.WithColumn("UpdatedAt").AsDateTime().Nullable();
为了清楚起见,以下列的 meaning/use:
- Id - 唯一订阅 ID。将用于退订
- AccountId - 用户订阅的帐户 ID。如果您希望所有这些都在帐户级别上工作,可以改为这样做
- UserId - 订阅用户的 Id
- EventType - 您的 Action 要响应的事件类型,例如 "new_form_submission"
- TargetURL - 订阅时zapier给你的目标URL。将在您 post 您的 JSON 发起操作时
- FormId - 提交时用户想要操作的表单 ID
这就是订阅所需的代码(显然你不能把它丢在那里让它工作 - 省去了很多钱 space)...
触发码
只剩下代码是实际的触发器代码 - 当您的代码中遇到要查找的事件时执行的代码。因此,例如,当用户提交 "form" 时,我们希望将该表单 JSON 发送到 Zapier。现在我们已经设置了所有其他代码,这部分非常简单。首先,检测我们有一个需要 Zapier 响应的提交到达的代码:
查看表单提交是否 registered/subscribed 到 Zapier 的实际代码:
public BusinessResult FormSubmitted(string jsonString)
{
var json = CommonUtils.DecodeJson(jsonString);
var account = _DBrepository.AccountRetrieveById(_currentUser.AccountId.Value); // Assumes user has bee authenticated
// inject additional meta data into json and retrieve submission/alert settings
var form = _DBformRepository.FormRetrieveById((int)json.formId);
// Lookup Subscription Webhooks
List<WebhookSubscription> subscriptions = _DBrepository.accountWebhookSubscriptions(account.Id);
if (subscriptions != null && subscriptions.Count > 0)
{
foreach (WebhookSubscription sub in subscriptions)
{
if (sub.EventType.Equals("new_form_submission") && sub.FormId == form.Id)
{
_webhookService.NewFormSubmission(sub, jsonString, form.Name, account.Name, account.Id);
}
}
}
}
最后是 post 的代码,将响应返回给 Zapier,Zapier 将解析 JSON 并将其发送给适当的各方:
public class WebhookService : IWebhookService
{
protected readonly IRepository _DBrepository;
public WebhookService(IRepository repository)
{
_DBrepository = repository;
}
public void NewFormSubmission(string formResultJSON)
{
throw new NotImplementedException();
}
public void NewFormSubmission(WebhookSubscription subscription, string formResultJSON, string formName, string accountName, int accountId)
{
// Now post to webhook URL
string response;
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.ContentType] = "application/json";
// Needs to be an array sent to Zapier
response = client.UploadString(subscription.TargetURL, "POST", "[" + formResultJSON + "]");
}
}
}
好的,这应该可以帮助您完成大部分工作。但是将 code/webhooks 连接到 Zapier 是变得有点困难的地方。现在的想法是使用 Development Dashboard 将上面的代码连接到您的 Zapier 应用程序中。您必须开始创建 Zapier 应用程序。您需要 2 个主要触发器 - 您尝试实施的基本操作(在本例中为 "New Form Submission")和身份验证,以便 Zapier 可以在用户创建 Zap 时对用户进行身份验证(在本例中为 "Test Auth" ).我使用的是 Basic Auth,但其他的也受支持(OAuth 等)。此外,我添加了一个触发器,它将 return 用户有权访问的表单列表。由于不是必需的,我不会在屏幕截图中显示该实现:
第 1 页
第 2 页
这是我连接表单列表的地方,它提供了一个表单列表,用户可以从中创建 Zap select。除非您有要显示的动态数据,否则您可能可以跳过它(将其留空)。为了完整起见,我将其包括在内:
第 3 页
这里连接测试数据
第 4 页
此页面允许您输入示例数据。跳过这个,因为它非常简单。
编写脚本API
现在您已经连接了您的第一个 Zap 触发器!但是等等,我们还没有完成。为了使订阅过程正常工作,我们需要添加一些脚本。这是整个过程中最难的部分,而且不是很直观。所以在原来的主屏幕上,向下一点你会看到 Scripting API:
现在您必须拥有用于 RESTHook 订阅的脚本。我不打算详细介绍,因为 Zapier 确实有这方面的文档,但很高兴知道 Zapier 确实将数据存储为订阅的一部分。在此之后我们还需要再做一个接线步骤...
var Zap = {
pre_subscribe: function(bundle) {
bundle.request.method = 'GET';
bundle.request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
bundle.request.params = {
target_url: bundle.subscription_url,
target_event:bundle.event,
form_id:bundle.trigger_fields.form_id
};
bundle.request.data = $.param({
target_url: bundle.subscription_url,
target_event:bundle.event,
form_id:bundle.trigger_fields.form_id
});
return bundle.request;
},
post_subscribe: function(bundle) {
// must return a json serializable object for use in pre_unsubscribe
var data = JSON.parse(bundle.response.content);
// we need this in order to build the {{webhook_id}}
// in the rest hook unsubscribe url
return {webhook_id: data.id};
},
pre_unsubscribe: function(bundle) {
bundle.request.method = 'DELETE';
bundle.request.data = null;
return bundle.request;
},
new_form_submission_pre_poll: function(bundle) {
bundle.request.method = 'GET';
bundle.request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
bundle.request.params = bundle.trigger_fields;
bundle.request.data = $.param({
form_id:bundle.trigger_fields.form_id
});
return bundle.request;
}
};
这有点道理...但是看看 Zapier 文档,它应该会有所帮助。或者 post 这里的问题,我会尽力回答...这比我预期的要大!
管理触发器设置
最后,我们需要完成订阅的连接...
然后我们设置我们之前创建的 RESTHook 方法:
就是这样。希望这会节省一些时间和学习经验!