Article HTTP Callbacks

HttpCallbacks allow plugins to respond directly to HTTP requests.

Why use an HttpCallback?

 HttpCallbacks are a platform supported alternative to HttpHandlers.  Additionally the platform validates requests against Cross-Site Request Forgery and user authentication is persisted to HttpCallbacks.

Creating an HttpCallback

To create a callback the  IHttpCallback interface must be implemented, this interface requires a reference to the Telligent.Evolution.Components.dll.  The IHttpCallback interface extends IPlugin to add support for handling HTTP requests.  

First, we will use the SetController method to get a reference to the IHttpCallbackController and set a private variable.  This will give us access to the controller and the ability to generate URLs for our callback plugin.

IHttpCallbackController _callbackController;

void IHttpCallback.SetController(IHttpCallbackController controller)
{
    _callbackController = controller;
}

Second, the ProcessRequest method will handle requests to the callback URL.  In this instance we will look for a querystring variable named "value", if found the code will return javascript that will write the value to the browser's console.  If  the "value" variable is not present in the callback URL, a 404 will be returned.

void IHttpCallback.ProcessRequest(System.Web.HttpContextBase httpContext)
{
    var value = String.Empty;

    if (httpContext.Request.QueryString["value"] != null)
        value = httpContext.Request.QueryString["value"].ToString(CultureInfo.InvariantCulture);

    if (!String.IsNullOrEmpty(value))
    {
        httpContext.Response.ContentType = "text/javascript";
        httpContext.Response.Write("alert('" + PublicApi.Html.EnsureEncoded(value) + "');");
    }
    else
    {
        httpContext.Response.StatusCode = 404;
        return;
    }
}

Here is the source code for our sample HttpCallback:

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;
using Telligent.Evolution.Extensibility.Api.Version1;
using Telligent.Evolution.Extensibility.UI.Version1;

namespace Telligent.Evolution.Examples
{
    public class SampleCallback : IHttpCallback
    {
        #region IHttpCallback Members

        void IHttpCallback.ProcessRequest(System.Web.HttpContextBase httpContext)
        {
            var value = String.Empty;

            if (httpContext.Request.QueryString["value"] != null)
                value = httpContext.Request.QueryString["value"].ToString(CultureInfo.InvariantCulture);

            if (!String.IsNullOrEmpty(value))
            {
                httpContext.Response.ContentType = "text/javascript";
                httpContext.Response.Write("alert('" + PublicApi.Html.EnsureEncoded(value) + "');");
            }
            else
            {
                httpContext.Response.StatusCode = 404;
                return;
            }
        }

        IHttpCallbackController _callbackController;

        void IHttpCallback.SetController(IHttpCallbackController controller)
        {
            _callbackController = controller;
        }
        #endregion

        #region IPlugin Members
        string Extensibility.Version1.IPlugin.Description
        {
            get { return "Uses an IHttpCallback to display messages in the browser console."; }
        }

        void Extensibility.Version1.IPlugin.Initialize()
        {

        }

        string Extensibility.Version1.IPlugin.Name
        {
            get { return "Sample Callback"; }
        }
        #endregion
    }
}

On its own, the HttpCallback can generate URLs and handle requests to those URLs, but we will need to add some more functionality to make use of the plugin.  With the addition of IHtmlHeaderExtension, we can output the callback URL as a script tag in the header of the page.  Our HttpCallback plugin can then respond to that request.

The Header extension uses the IHttpCallbackController variable to generate the correct URLs for the HttpCallback plugin. The sample code will render one script tag with a value querystring and a second script tag without a querystring.

string IHtmlHeaderExtension.GetHeader(RenderTarget target)
{
    var javascript1 = new NameValueCollection();
    javascript1["value"] = "Sample Callback Response";

    var sb = new StringBuilder();

    sb.AppendFormat(@"<script src=""{0}""></script>", _callbackController.GetUrl(javascript1));
    sb.AppendFormat(@"<script src=""{0}""></script>", _callbackController.GetUrl());

    return sb.ToString();
}

bool IHtmlHeaderExtension.IsCacheable
{
    get { return true; }
}

bool IHtmlHeaderExtension.VaryCacheByUser
{
    get { return false; }
}

Here is the completed source code containing both the HttpCallback and the HtmlHeaderExtension implementations:

using System;
using System.Collections.Specialized;
using System.Globalization;
using System.Text;
using Telligent.Evolution.Extensibility.Api.Version1;
using Telligent.Evolution.Extensibility.UI.Version1;

namespace Telligent.Evolution.Examples
{
    public class SampleCallback : IHttpCallback, IHtmlHeaderExtension
    {
        #region IHttpCallback Members

        void IHttpCallback.ProcessRequest(System.Web.HttpContextBase httpContext)
        {
            var value = String.Empty;

            if (httpContext.Request.QueryString["value"] != null)
                value = httpContext.Request.QueryString["value"].ToString(CultureInfo.InvariantCulture);

            if (!String.IsNullOrEmpty(value))
            {
                httpContext.Response.ContentType = "text/javascript";
                httpContext.Response.Write("alert('" + PublicApi.Html.EnsureEncoded(value) + "');");
            }
            else
            {
                httpContext.Response.StatusCode = 404;
                return;
            }
        }

        IHttpCallbackController _callbackController;

        void IHttpCallback.SetController(IHttpCallbackController controller)
        {
            _callbackController = controller;
        }
        #endregion

        #region IHtmlHeaderExtension Members
        string IHtmlHeaderExtension.GetHeader(RenderTarget target)
        {
            var javascript1 = new NameValueCollection();
            javascript1["value"] = "Sample Callback Response";

            var sb = new StringBuilder();

            sb.AppendFormat(@"<script src=""{0}""></script>", _callbackController.GetUrl(javascript1));
            sb.AppendFormat(@"<script src=""{0}""></script>", _callbackController.GetUrl());

            return sb.ToString();
        }

        bool IHtmlHeaderExtension.IsCacheable
        {
            get { return true; }
        }

        bool IHtmlHeaderExtension.VaryCacheByUser
        {
            get { return false; }
        }
        #endregion

        #region IPlugin Members
        string Extensibility.Version1.IPlugin.Description
        {
            get { return "Uses an IHttpCallback to display messages in the browser console."; }
        }

        void Extensibility.Version1.IPlugin.Initialize()
        {

        }

        string Extensibility.Version1.IPlugin.Name
        {
            get { return "Sample Callback"; }
        }
        #endregion
    }
}

Once this code is deployed to our Verint Community site and the Sample Callback plugin is enabled, browsing to a page should result in a javascript alert being displayed on the site with the message Sample Callback Response.  The second callback request with no querystring should result in a 404 error and not a 2nd alert message.