How to: Create a Custom External Application Provider

This topic provides an overview of the process of creating a custom External Application Provider (EAP).

Applies to: SharePoint Foundation 2010

Creating a custom EAP requires deriving new classes from one or both of the two abstract classes:

  • SPExternalApplicationRequestResult – There are two scenarios in which you would implement a SPExternalApplicationRequestResult-derived class:

    • The site and request forwarder where the external application is installed require that all results that it receives from Microsoft SharePoint Foundation include a client hash that the request forwarder can verify to ensure that the result has not been tampered with.

    • You want to change the way the SilverlightWebPart, or some custom Web Part that hosts a non-SharePoint application, is rendered; for example, you want to render custom chrome around the Web Part.

  • SPExternalApplicationProvider – You will need to implement this class in any of the following scenarios:

    • Any situation in which you are implementing a class derived from the SPExternalApplicationRequestResult class (see above).

    • The External Application XML for the Web Part contains custom markup that needs to be read and processed.

    • You want special logic to run when the Web Part’s child control is created.

    • You want to customize the UI of the SilverlightToolPart or change the application registration page that opens when the Configure button on the tool part is clicked.

Important

When considering whether to create a custom EAP, keep in mind that there can be only one EAP for all the application-hosting Web Parts in all the SharePoint Foundation Web applications that are children of a specified Web service.

To Implement a Request Result Class

  1. In Microsoft Visual Studio 2010, start an empty SharePoint project.

  2. Add a class item to the project.

  3. Set the class to inherit from SPExternalApplicationRequestResult.

  4. If you do not want a client hash, implement the ClientHash property to return null. If you want to include a client hash in the results that are sent back to the external application, implement the ClientHash property as a wrapper around a private backing field that is a Byte array.

    private Byte[] clientHash;
    public override Byte[] ClientHash 
    {
        get { return clientHash; } 
    }
    
    Private clientHash_Renamed() As Byte
    Public Overrides ReadOnly Property ClientHash() As Byte()
        Get
            Return clientHash_Renamed
        End Get
    End Property
    
  5. If you are using a client hash, create a constructor that takes a parameter of type SPExternalApplicationRequestProperties and a second parameter that is a byte array (which represents the client salt). This constructor should use the RequestTokenPrefix property of the SPExternalApplicationRequestProperties object and the byte array as input when creating a client hash value. The byte array must be a value that is provided by server that hosts the external application and the algorithm used must be the same one that will be used by the external application to create its copy of the client hash. Consider using the classes of the System.Security.Cryptography namespace such as HashAlgorithm or SHA512Managed. The following is an example. To use the SHA512Managed class as this example does, you must add a using (Imports in Visual Basic) statement for the System.Security.Cryptography namespace to the code file.

    public CustomRequestResult() { }
    public CustomRequestResult(SPExternalApplicationRequestProperties externalAppRequest, byte[] clientSalt)
    {
        string prefix = externalAppRequest.RequestTokenPrefix;
    
        int nCount = Encoding.Unicode.GetByteCount(prefix);
        nCount += clientSalt.Length;
        byte[] bytes = new byte[nCount];
        nCount = Encoding.Unicode.GetBytes(prefix, 0, prefix.Length, bytes, 0);
        for (int i = 0; i < clientSalt.Length; i++)
        {
            bytes[nCount + i] = clientSalt[i];
        }
        // Compute the hash value
        SHA512Managed sha512 = new SHA512Managed();
        clientHash = sha512.ComputeHash(bytes);
    }
    
    Public Sub New()
    End Sub
    
    Public Sub New(ByVal externalAppRequest As SPExternalApplicationRequestProperties, ByVal clientSalt() As Byte)
        Dim prefix As String = externalAppRequest.RequestTokenPrefix
    
        Dim nCount As Integer = Encoding.Unicode.GetByteCount(prefix)
        nCount += clientSalt.Length
        Dim bytes(nCount - 1) As Byte
        nCount = Encoding.Unicode.GetBytes(prefix, 0, prefix.Length, bytes, 0)
        For i As Integer = 0 To clientSalt.Length - 1
            bytes(nCount + i) = clientSalt(i)
        Next i
        ' Compute the hash value
        Dim sha512 As New SHA512Managed()
        clientHash = sha512.ComputeHash(bytes)
    End Sub
    

    If the only part of the SPExternalApplicationRequestProperties class that your derived class uses is the RequestTokenPrefix property, then the constructor could be written to take a String object as the first parameter and then calling code (which is the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method discussed below) simply passes the RequestTokenPrefix property to it. Another option is to design the calling code to create the client hash before it constructs an object of your derived type. In that case, the constructor of the type can be designed to take the hash value itself as a byte array parameter and write immediately to the clientHash backing field.

  6. If you do not want to customize how the Silverlight Web Part is rendered or how some custom Web Part that hosts a non-SharePoint application, is rendered, implement the GetContentControl(String) method to return null. But if you do want to customize how the Web Part is rendered, implement the method to return a Control that will be the sole child control of the Web Part. Typically this will be a Literal containing HTML markup.

To Implement an EAP Class

  1. Add a class item to the project.

  2. Set the class to inherit from SPExternalApplicationProvider.

  3. If you want to customize the UI of the SilverlightToolPart, implement the GetRegistrationInformation(SPWeb) method. You can change any of the five properties of the SPExternalApplicationRegistrationInformation object that the method returns. For example, if you wanted to substitute a custom registration page, assign the URL of your custom page to the HyperlinkUrl property. You can use inline code or code behind the custom page to provide other services to users. For example, your code could read the Application XML and see if it specifies an application principal name. If so, the code checks to see if that application principal user exists and, if not, creates it.

    The following example changes the name of the button on the page from "Configure" to "Register", the instructional text for the button, and the size of the dialog that it opens. It also causes the button to open a custom alternative application registration page. (The default registration page is newslwp.aspx.)

    public override SPExternalApplicationRegistrationInformation GetRegistrationInformation(SPWeb web)
    {
        SPExternalApplicationRegistrationInformation info = new SPExternalApplicationRegistrationInformation();
        info.Description = "To register a Silverlight application (.xap), click Register";
        info.DialogHeight = 600;
        info.DialogWidth = 500;
        string url = web.ServerRelativeUrl;
        if (!url.EndsWith("/"))
        {
            url = url + "/";
        }
        url += "_layouts/alternateReg.aspx";
        info.HyperlinkText = "Register";
        info.HyperlinkUrl = url;
    
        return info;
    }
    
    Public Overrides Function GetRegistrationInformation(ByVal web As SPWeb) As SPExternalApplicationRegistrationInformation
        Dim info As New SPExternalApplicationRegistrationInformation()
        info.Description = "To register a Silverlight application (.xap), click Register"
        info.DialogHeight = 600
        info.DialogWidth = 500
        Dim url As String = web.ServerRelativeUrl
        If Not url.EndsWith("/") Then
            url = url & "/"
        End If
        url &= "_layouts/alternateReg.aspx"
        info.HyperlinkText = "Register"
        info.HyperlinkUrl = url
    
        Return info
    End Function
    
  4. If you did not derive a class from SPExternalApplicationRequestResult, then you can implement OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) to simply return null. Otherwise, you must give this method a substantial implementation so that your request result class is constructed and used.

    The OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method is called by the Web Part’s CreateChildControls method. Specifically, the CreateChildControls method does the following:

    1. Constructs a SPExternalApplicationRequestProperties object from the External Application XML that was used to register the Web Part and from information about the current Web site.

    2. Passes the a SPExternalApplicationRequestProperties object to the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method.

    3. Receives a SPExternalApplicationRequestResult-derived object that is returned by the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method.

    4. Uses the GetContentControl(String) method of the request result object to obtain the Web Part’s sole child control.

    Figure 1: Calls made by the CreateChildControls method.

    Silverlight Web Part CreateChildControls

    If, for any reason, the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method returns null, as it does in the default EAP that is built into SharePoint Foundation, then the CreateChildControls method will need to render a default child control. This is what the CreateChildControls() method of the built-in SilverlightWebPartclass does.

    Therefore, the primary job of your implementation of the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method is to call the constructor of your SPExternalApplicationRequestResult-derived class and return the constructed object. Here are some points to keep in mind when developing your override of the OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) method:

    In the following example, the overridden OnApplicationWebPartCreateChildControls(SPExternalApplicationRequestProperties) calls the nondefault constructor of a SPExternalApplicationRequestResult-derived class called CustomRequestResult. For more about this nondefault constructor see the procedure To Implement a Request Result Class earlier in this topic.

    public override SPExternalApplicationRequestResult OnApplicationWebPartCreateChildControls(
                SPExternalApplicationRequestProperties args)
    {
        SPExternalApplicationRequestResult reqRes = CustomRequestResult(args, saltFromApplication);
        return reqRes;
    }
    
    Public Overrides Function OnApplicationWebPartCreateChildControls(ByVal args As SPExternalApplicationRequestProperties) As SPExternalApplicationRequestResult
            Dim reqRes As SPExternalApplicationRequestResult = CustomRequestResult(args, saltFromApplication)
            Return reqRes
    End Function
    

Identifying Your EAP to the Web Service

The process of identifying to SharePoint Foundation your custom EAP is virtually the same as the process for enabling an EAP as described in How to: Enable an External Application Provider. The only difference is that instead of simply enabling an EAP, you construct an object of your EAP type and assign it to the ExternalApplicationSettings.Provider property. The following is an example, where ContosoEAP is a class derived from SPExternalApplicationProvider.

ContosoEAP exAppProvider = new ContosoEAP();
SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider;
SPWebService.ContentService.ExternalApplicationSettings.Enabled = true;
SPWebService.ContentService.Update(); 
Dim exAppProvider As New ContosoEAP()
SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider
SPWebService.ContentService.ExternalApplicationSettings.Enabled = True
SPWebService.ContentService.Update()

Tip

The line that sets the Enabled property is not needed if external application management has already been enabled for the Web service, but it does no harm. If management has not previously been enabled, including it obviates the need for the procedure in How to: Enable an External Application Provider. However, the converse is not the case, even if the default EAP is being used, the management enabling code must be executed.

You have available all the methods for running the preceding lines of code that are mentioned in How to: Enable an External Application Provider. The following example shows how to run the code with the Windows PowerShell Add-Type cmdlet in a Windows PowerShell command-line interface script.

To Identify Your EAP to the Web Service

  1. Add the following to a text file.

    Add-type @"
    using System;
    using Microsoft.SharePoint.Administration;
    
    namespace ContosoCmdlets
    
        public class EAPIdentifier
        {
            public static void IdentifyEAP()
            {
                ContosoEAP exAppProvider = new ContosoEAP();
                SPWebService.ContentService.ExternalApplicationSettings.Provider = exAppProvider;
                SPWebService.ContentService.ExternalApplicationSettings.Enabled = true;
                SPWebService.ContentService.Update(); 
            }
        }
    "@ -Language CsharpVersion3
    [ContosoCmdlets.EAPIdentifier]::IdentifyEAP()
    
  2. Save the file as EAPIdentify.ps.

  3. Run the script in a Windows PowerShell window.