2015年10月6日 星期二

Implement WebHook with OWIN Authentication

 ASP.NET   WebHook   Token based authentication   Web Api

背景


ASP.NET 即將加入新的WebHooks 開發架構,
目前(2015/10/6)相關package仍在Beta階段,因此學習資源也不多。

但是可以預見未來發佈後, 在開發上應該會非常方便。

但是如果在Server已有認證的服務,例如:OWIN Authetication,只要加上註冊通知的URL””Callback”服務,即可達成WebHook的效果。



相關文章



環境

l   Visual Studio 2015
l   WEB API 2.2
l   Entity Framework 6


實作


Authentication Service

首先,我們必須實作驗證的部分,請參考上列的參考文章建立一個OWIN Identity Authentication Server
目的是用Server提供驗證後的Token以確保使用WebHook ServiceClient side都已經經過認證。

Data Access Layer

此例將在Http Callback要通知的客戶資訊,紀錄於資料庫。
因此先實作DAL Code First的方式。 沒有太特殊的部分,直接列出POCODbContext


POCO

public class WebHookCallback : BaseEntity
{
        [Key]
        [Column(Order = 1)]
        [Display(Name = "序號")]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int WebHookCallbackId { get; set; }

        [Required]
        [Column(Order = 2)]
        [Display(Name = "Callback URI")]
        public String Uri { get; set; }

        [Column(Order = 3)]
        [Display(Name = "建立者")]
        public String CreateBy { get; set; }

        [Required]
        [Column(Order = 4)]
        [Display(Name = "是否啟用")]
        public bool IsEnabled { get; set; }
}


public class BaseEntity
{
        /// <summary>
        /// 建立日期
        /// </summary>
        public DateTime CreateOn { get; set; }
        /// <summary>
        /// 更新日期
        /// </summary>
        public DateTime UpdateOn { get; set; }

        public BaseEntity()
        {
            CreateOn = DateTime.Now;
            UpdateOn = DateTime.Now;
}
    }


l   DbContext

public class WebHookContext : DbContext
{
        public WebHookContext()
                     : base("name=WebHookDbContext"//指定對應的Connection String名稱
        { }

        public DbSet<WebHookCallback> WebHookCallbacks { get; set; }
       
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            //Create the relation between the tables (POCOs)
        }
}



Database service

建立一支負責處理Database CRUD的服務介面。

public class WebHookCallbackService : IDisposable
{
        private WebHookContext _dbContext = null;

        public WebHookCallbackService()
        {
            this._dbContext = new WebHookContext();
        }


        public void Insert(WebHookCallback entity, out int? id)
        {
            this._dbContext.WebHookCallbacks.Add(entity);
            this._dbContext.SaveChanges();

            id = entity.WebHookCallbackId;
        }

        public void Remove(int webhookCallbackId)
        {
            var entity = this._dbContext.WebHookCallbacks.Where(x => x.WebHookCallbackId.Equals(webhookCallbackId))
                .FirstOrDefault();
            entity.IsEnabled = false;

            this._dbContext.SaveChanges();
        }

        public IQueryable<WebHookCallback> Get(Func<WebHookCallback, bool> filter)
        {
            var entities = this._dbContext.WebHookCallbacks.Where(filter).AsQueryable();
            return entities;
        }

        public void Dispose()
        {
            this._dbContext.Dispose();
        }
}


Implement the We Api which supporting WebHook

有了DAL和介面後,接下來可專心實作中心端支援WebHookWeb Api 至少需產生以下方法:

1.  Register : 註冊需要通知的客戶端資訊
2.  Unregister:移除不必要通知的客戶端資訊
3.  TriggerCallback 用來測試,觸發CallbackPost 方法

[EnableCors(origins: "*", headers: "*", methods: "*")]
public class WebHookController : ApiController
{
        [Authorize]
        [HttpPost]
        public IEnumerable<int?> Register(RegisterInfo regInfo)
        {
            int? registeredId = null;

            if(String.IsNullOrEmpty(regInfo.WebHookUri))
            {
                this.Request.CreateResponse(HttpStatusCode.BadRequest);
            }
            else
            {
                using (var whService = new WebHookCallbackService())
                {
                    whService.Insert(PocoFactory.CreateWebHookCallback(regInfo), out registeredId);
                }
            }

            return new int?[] { registeredId };
        }

        [Authorize]
        [HttpPost]
        public void Unregister(int id)
        {
            using (var whService = new WebHookCallbackService())
            {
                whService.Remove(id);
            }
        }

        [HttpPost]
        public void TriggerCallback(CallbackMsg callbackMsg)
        {
            using (var whService = new WebHookCallbackService())
            {
                var webhookEntities = whService.Get(
x=>!String.IsNullOrEmpty(x.Uri) && x.IsEnabled==true);

                Parallel.ForEach(webhookEntities, entity =>
                {

                    String apiUrl = entity.Uri;
                    this.sendCallbackRequestToClient(apiUrl, callbackMsg);
                }
                );


                Thread.Sleep(5000);
            }
        }


        private void sendCallbackRequestToClient(String apiUrl, CallbackMsg callbackMsg)
        {
            //Intial HttpClient
            HttpClient httpClient = new HttpClient();
            //Set the http service uri
            httpClient.BaseAddress = new Uri(apiUrl);
            //The http service is based on JSON format
            httpClient.DefaultRequestHeaders.Accept.Add(
                   new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

            var msg = String.Empty;
            HttpResponseMessage resp = httpClient.PostAsJsonAsync(apiUrl, callbackMsg).Result;

            Debug.WriteLine(String.Format("{0}:{1}", apiUrl, resp.StatusCode.ToString()));
        }
}


Implement the receiver for WebHook in client sides

最後實作另外一支Web Api,加入WebHookCallback postReceiver Http methods.
以下面的程式碼為例:

public class ReceiveController : ApiController
{
        [HttpPost]
        public HttpResponseMessage Receive1()
        {
            return this.Request.CreateResponse(HttpStatusCode.OK);
        }


        [HttpPost]
        public HttpResponseMessage Receive2()
        {
            return this.Request.CreateResponse(HttpStatusCode.OK);
        }
}




測試



取得Token

先註冊OWIN Authentication,成功後取得Token

 




註冊WebHook服務


 




測試註冊五個Callback post url的結果。


 


註銷WebHook服務

 






測試Http Callback




Server side收到Client的回覆結果。

 


Reference




沒有留言:

張貼留言