<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/"><channel><title>Custom REST Host</title><link>https://community.telligent.com/community/11/w/developer-training/65133/custom-rest-host</link><description /><dc:language>en-US</dc:language><generator>14.0.0.586 14</generator><item><title>Custom REST Host</title><link>https://community.telligent.com/community/11/w/developer-training/65133/custom-rest-host</link><pubDate>Thu, 13 Jun 2019 19:32:47 GMT</pubDate><guid isPermaLink="false">715dbf3b-2ed5-4e03-bed1-d342da4cb47e</guid><dc:creator>Ben Tiedt</dc:creator><comments>https://community.telligent.com/community/11/w/developer-training/65133/custom-rest-host#comments</comments><description>Current Revision posted to Developer Training by Ben Tiedt on 06/13/2019 19:32:47&lt;br /&gt;
&lt;p&gt;If for some reason the hosts provided by the SDK are not sufficient you can create your own rest host. &amp;nbsp; It is rare that you should have to do this but it an option for specific scenarios. &amp;nbsp;This article assumes you are referencing the latest [[REST SDK]] in your project.&lt;/p&gt;
&lt;p&gt;[toc]&lt;/p&gt;
&lt;h2&gt;&lt;a id="Creating_Your_Class" name="Creating_Your_Class"&gt;&lt;/a&gt;Creating Your Class&lt;/h2&gt;
&lt;p&gt;As mentioned in the [[Hosts]] article, all hosts inherit&amp;nbsp;&lt;code&gt;Telligent.Evolution.Extensibility.Rest.Version1.RestHost&lt;/code&gt;. &amp;nbsp;This class encapsulates the REST communication framework while providing members for custom hosts to implement individually. &amp;nbsp; You should never try and implement anything lower level then the &lt;code&gt;RestHost&lt;/code&gt; abstract class.&lt;/p&gt;
&lt;p&gt;For this example we will implement a host that does not use OAuth at all and instead uses API keys to authenticate users. &amp;nbsp;It is important to note that OAuth is still preferred however for some utility aspects API keys are easier and acceptable to use. &amp;nbsp;If you are integrating with a production website or application you should not use API keys. &amp;nbsp;To can learn more by reviewing the [[Authentication|Rest Authentication]] topic.&lt;/p&gt;
&lt;p&gt;Create a new class called &lt;code&gt;APIKeyRESTHost&lt;/code&gt; that inherits from&amp;nbsp;&lt;code&gt;Telligent.Evolution.Extensibility.Rest.Version1.RestHost&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;
namespace Samples
{
    public class APIKeyRestHost : Telligent.Evolution.Extensibility.Rest.Version1.RestHost
    {
    }
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;If you right click &lt;code&gt;Telligent.Evolution.Extensibility.Rest.Version1.RestHost&lt;/code&gt; in the class definition in Visual Studio and select &amp;quot;Implement Abstract Class&amp;quot; you will see it adds 3 members.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;namespace Samples
{
    public class APIKeyRestHost : Telligent.Evolution.Extensibility.Rest.Version1.RestHost
    {
        public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
        {
            throw new NotImplementedException();
        }

        public override string EvolutionRootUrl
        {
            get { throw new NotImplementedException(); }
        }

        public override string Name
        {
            get { throw new NotImplementedException(); }
        }
    }
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;strong&gt;ApplyAuthenticationToHostRequest&lt;/strong&gt;&lt;/code&gt; is the method called by each REST call to add the appropriate authentication headers. &amp;nbsp;This will be convered in detail but here we will determine the logged on user and apply the impersonation and API key headers to the request.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;strong&gt;EvolutionRootUrl&lt;/strong&gt;&lt;/code&gt; is the base URL&amp;nbsp;to your community used for all requests going forward.(e.g. http://community.mysite.com/).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&lt;strong&gt;Name&lt;/strong&gt;&lt;/code&gt; is used to identify the host in a friendly way. &amp;nbsp;In a custom host it has no specific functional value unless it is used by the implementer. &amp;nbsp;For example the [[Default REST Host|Default Rest Host]] stores multiple hosts in memory and uses this property to reference and retrieve specific instances. &amp;nbsp;Even if not used a value should still be returned.&lt;/p&gt;
&lt;h2&gt;&lt;a id="Implementing_the_API_Key_Host" name="Implementing_the_API_Key_Host"&gt;&lt;/a&gt;Implementing the API Key Host&lt;/h2&gt;
&lt;p&gt;Start by adding the name by hardcoding &amp;quot;APIKeyRestHost&amp;quot; as the property return value. &amp;nbsp;As mentioned we are not going to be using this field in this implementation so there is no need to make this dynamic.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;public override string Name
{
   get { return &amp;quot;APIKeyRestHost&amp;quot;; }
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a id="Getting_Required_Information" name="Getting_Required_Information"&gt;&lt;/a&gt;Getting Required Information&lt;/h3&gt;
&lt;p&gt;You could also hardcode the URL&amp;nbsp;to your community in the EvolutionRootUrl property but generally this isn&amp;#39;t a good practice. &amp;nbsp;Especially if you plan on using this tool for multiple communities or different environments. &amp;nbsp;So we are going to gather this information dynamically.&lt;/p&gt;
&lt;p&gt;Additionally we need to get some other pieces of information that are required for this host to work:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Admin API Key&lt;/strong&gt; - &amp;nbsp;This is the default key we will use. &amp;nbsp;For simplicity&amp;#39;s sake we are using an admin account however for this implementation this API key user minimally needs only impersonate user and view profile permissions in your community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Admin Username&lt;/strong&gt; - The username associated to the admin API key mentioned above.&lt;/p&gt;
&lt;p&gt;These pieces of information are used by our host whenever a [[Making Requests|call to REST is made from the host]] that is set to not impersonate. &amp;nbsp;One thing to keep in mind is that a consumer of your host can make any call without impersonation (so using the admin API key in our case)&amp;nbsp;which means depending on the security level you give to this API key user you may be exposing too much or too little.&lt;/p&gt;
&lt;p&gt;Since these are all community specific, we will pass them in as part of the constructor and then use the Url value for the required &lt;code&gt;EvolutionRootUrl&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;namespace Samples
{
    public class APIKeyRestHost : Telligent.Evolution.Extensibility.Rest.Version1.RestHost
    {
        private string _communityUrl = null;
        private string _adminAPIKey = null;
        private string _adminUserName = null;

        public APIKeyRestHost(string communityUrl, string adminUserName, string adminApiKey)
        {
            _communityUrl = communityUrl;
            _adminAPIKey = adminApiKey;
            _adminUserName = adminUserName;
        }
        
        public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
        {
            
        }

        public override string EvolutionRootUrl
        {
            get { return _communityUrl; }
        }

        public override string Name
        {
            get { return &amp;quot;APIKeyRestHost&amp;quot;; }
        }
    }
}&lt;/pre&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a id="Adding_Authentication_Headers" name="Adding_Authentication_Headers"&gt;&lt;/a&gt;Adding Authentication Headers&lt;/h3&gt;
&lt;p&gt;You will need to understand API Key authentication for this which can be found by reviewing the [[Authentication|REST Authentication]] topic.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ApplyAuthenticationToHostRequest&lt;/code&gt; is the most involved member. &amp;nbsp;It is here that we apply the authentication headers we gathered in the constructor and deal with impersonation. &amp;nbsp;First we must always apply the API key information we gathered in the constructor to every request. &amp;nbsp;Notice that the &lt;code&gt;HttpWebRequest&lt;/code&gt; is given to you as an argument to this method.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
{
    var adminKey = String.Format(&amp;quot;{0}:{1}&amp;quot;, _adminAPIKey, _adminUserName);
    var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));

    request.Headers.Add(&amp;quot;Rest-User-Token&amp;quot;, adminKeyBase64);
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Next, we use the &lt;code&gt;forAccessingUser&lt;/code&gt; boolean argument to determine if we need to impersonate. &amp;nbsp; If it&amp;#39;s false the we will not be adding any additional information. &amp;nbsp;If it&amp;#39;s true we need to impersonate. &amp;nbsp;For this example we are going to use the &lt;code&gt;HttpContext&lt;/code&gt; user and this host will only work if the user is logged in. &amp;nbsp;Notice there is a &lt;code&gt;GetCurrentHttpContext&lt;/code&gt;() method built in to the parent class to get an &lt;code&gt;HttpContextBase&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;We will obtain the username from context and then do a lookup of that&amp;nbsp;username in community to make sure it exists and if not throw an exception. &amp;nbsp;You could also implement logic that automatically creates the user here but for this example we assume they are already in the community user base. &lt;strong&gt;Remember to set the &lt;code&gt;enableImpersonation&lt;/code&gt; flag on the &lt;code&gt;GetToDynamic&lt;/code&gt; call to false or your application will end up in an endless loop towards disaster!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
{
    var adminKey = String.Format(&amp;quot;{0}:{1}&amp;quot;, _adminAPIKey, _adminUserName);
    var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));
    request.Headers.Add(&amp;quot;Rest-User-Token&amp;quot;, adminKeyBase64);
    
    if (forAccessingUser)
    {
        var httpContext = this.GetCurrentHttpContext();
        if (!httpContext.User.Identity.IsAuthenticated)
            throw new AuthenticationException(&amp;quot;This REST Host does not support anonymous access.&amp;quot;);
    
        var username = httpContext.User.Identity.Name;
        var user = this.GetToDynamic(2, &amp;quot;users/{username}.json&amp;quot;, false, new RestGetOptions()
        {
            PathParameters = { { &amp;quot;username&amp;quot;, username } }
        });
    
        if (user == null)
            throw new ApplicationException(string.Format(&amp;quot;User &amp;#39;{0}&amp;#39; wasn&amp;#39;t found.  Create this user in your community first&amp;quot;));
     }      
}&lt;/pre&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If the user is found use the username in the impersonate header.&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;public override void ApplyAuthenticationToHostRequest(System.Net.HttpWebRequest request, bool forAccessingUser)
{
    var adminKey = String.Format(&amp;quot;{0}:{1}&amp;quot;, _adminAPIKey, _adminUserName);
    var adminKeyBase64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(adminKey));

    request.Headers.Add(&amp;quot;Rest-User-Token&amp;quot;, adminKeyBase64);
    if (forAccessingUser)
    {
        var httpContext = this.GetCurrentHttpContext();
        if (!httpContext.User.Identity.IsAuthenticated)
            throw new AuthenticationException(&amp;quot;This REST Host does not support anonymous access.&amp;quot;);
    
        var username = httpContext.User.Identity.Name;
        var user = this.GetToDynamic(2, &amp;quot;users/{username}.json&amp;quot;, false, new RestGetOptions()
        {
            PathParameters = { { &amp;quot;username&amp;quot;, username } }
        });
    
        if (user == null)
            throw new ApplicationException(string.Format(&amp;quot;User &amp;#39;{0}&amp;#39; wasn&amp;#39;t found.  Create this user in your community first&amp;quot;));
      
        //Apply impersonation
        request.Headers.Add(&amp;quot;Rest-Impersonate-User&amp;quot;, username);
     }      
}&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;Here is the host all put together:&lt;/p&gt;
&lt;p&gt;&amp;nbsp;&lt;a href="https://community.telligent.com/cfs-file/__key/communityserver-wikis-components-files/00-00-00-12-83/APIKeyHost_2E00_cs"&gt;community.telligent.com/.../APIKeyHost_2E00_cs&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;&lt;a id="Using_The_Host" name="Using_The_Host"&gt;&lt;/a&gt;Using The Host&lt;/h2&gt;
&lt;p&gt;Now that the host is implemented it is simply a metter of constructing an instance of our API Key host and invoking one of the [[Making Requests|REST calls]] supported by the SDK host:&lt;/p&gt;
&lt;p&gt;&lt;pre class="ui-code" data-mode="csharp"&gt;var host = new APIKeyRestHost(&amp;quot;http://yourcommunityurl.com&amp;quot;, &amp;quot;[Admin USername]&amp;quot;, &amp;quot;[Admin API Key]&amp;quot;);

//Call an API
var user = host.GetToDynamic(2, &amp;quot;users/{username}.json&amp;quot;, true,new RestGetOptions()
{
    PathParameters = { { &amp;quot;username&amp;quot;,&amp;quot;pmason&amp;quot;} }
});&lt;/pre&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;
</description></item></channel></rss>