Thursday, January 7, 2010

Walkthrough: Creating and Packaging a Custom Web Service for SharePoint

Here's a walk-through of how to create a custom Web service that operates within the context of Windows SharePoint Services 3.0. It steps through the process of creating a simple "Hello World" Web service and then shows how to modify the service so that it implements the Windows SharePoint Services 3.0 server-side object model to return site and list data. Finally, this guide will detail how you can package this up as a SharePoint solution file so that it can be deployed to the _vti_bin (12 hive ISAPI) folder as per best practices.

 

Basic Steps for Creating a Web Service

  • Create an ASP.NET Web service in Microsoft Visual Studio 2005.
  • Create a class library within the Web service that defines the programming logic for the Web service.
  • Generate and edit a static discovery file and a Web Services Description Language (WSDL) file.
  • Package and deploy the Web service files to the _vti_bin directory. (12 hive ISAPI folder)
  • Create a client application to consume the Web service.
  • How to implement SharePoint object model functionality in your custom web service.

Creating an ASP.NET Web Service in Visual Studio

The first step is to create an ASP.NET Web service Web site in Visual Studio.

To create an ASP.NET Web service

1.     In Visual Studio, click File, point to New, and then select Web Site.

2.     In the Templates box of the New Web Site dialog box, select ASP.NET Web Service, select File System in the Location box, select a programming language and location for the project, and then click OK.

3.     Within the new Web service solution, create a separate class library project to contain the Web service logic. To create the project, click File, point to New, and then select Project.

4.     In the New Project dialog box, select a language in the Project types box, select Class Library in the Templates box, provide a name and location for the project, select Add to Solution in the Solution box, and then click OK.

5.     Add a reference to the System.Web.Services namespace in the class library project. Right-click the project in Solution Explorer, click Add Reference, select System.Web.Services in the Add Reference dialog box, and then click OK.

6.     Replace the default class file in the class library project with the default service class file that Visual Studio provides in the App_Code folder of the Web service.

To replace the class file with the service class file

a.     In Solution Explorer, drag Service.vb to the top node in the class library project.

b.     Delete the Class1.vb file, and also delete the Service.vb file that remains in the App_Code folder.

7.     Create a strong name for the class library:

a.     In Solution Explorer, right-click the class library project, and then click Properties.

b.     In the Properties dialog box, click Signing, select Sign the assembly, and then select <New> in the Choose a strong name key file list.

c.     In the Create Strong Name Key dialog box, provide a file name for the key, clear the Protect my key file with a password check box, and then click OK.

8.     To build only the class library project, right-click the project in Solution Explorer, and then click Build.

9.     In order to be able to run your web service you must either add your class library assembly to the GAC or set a project reference in the web service project to the class library assembly.

10.  Now you are ready to modify the assembly information in the default Service.asmx file of the Web service with information for the DLL from the GAC or project reference. You will need to know the GUID of the class library. Get this from the properties of the GAC’d assembly or by running the ‘sn.exe –Tp assembly’ command on your class library assembly.

11.  To open Service.asmx in Solution Explorer, right-click the file and click Open.

12.  Remove the CodeBehind attribute from the page directive in Service.asmx, and modify the contents of the Class attribute so that the directive matches the following format, where the assembly name "MyServiceAssembly" and the public key token are values specified in the Properties dialog box that you opened in step 10:

<%@ WebService Language="VB" Class=" MyServiceNamespace.Service, MyServiceAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=0057f584d6eb427a" %>

 

13.  Save your changes.

Generating and Modifying Static Discovery and WSDL Files

To provide discovery and description for your custom Web service, you must create a .disco file and a .wsdl file. Because Windows SharePoint Services virtualizes its URLs (for example, http://MyServer/MySite/MySubsite becomes http://MyServer), you cannot use the autogenerated .disco and .wsdl files generated by ASP.NET. Instead, you must create a .disco page and a .wsdl ASPX page that provides the necessary redirection and maintains virtualisation.

You can generate the .disco and .wsdl files by running the web service project and then using the .NET Framework Web Service Discovery tool (Disco.exe) to obtain the generated files.

To generate the static discovery and WSDL files

1.     Press <F5> on the web service project to start the web service and then copy the generated URL in the web services test page which opens.

2.     Run Disco.exe at the command prompt from web service project directory to generate .disco and .wsdl files. Run the command in the following format to generate the files in the web service project directory (where the url is the copied URL from the web service test page)

disco URL

3.     To register namespaces of the Windows SharePoint Services object model, open both the .disco and .wsdl files and replace the opening XML processing instruction -- <?xml version="1.0" encoding="utf-8"?> -- with the following:

<%@ Page Language="VB" Inherits="System.Web.UI.Page" %>

<%@ Assembly Name="Microsoft.SharePoint, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Import Namespace="Microsoft.SharePoint.Utilities" %>

<%@ Import Namespace="Microsoft.SharePoint" %>

<% Response.ContentType = "text/xml" %>

 

4.     In the .disco file, modify the contract reference and SOAP address tags to be like the following example, which replaces literal paths with code generated paths through use of the Microsoft.SharePoint.Utilities.SPHttpUtility class, and which replaces the method name that is specified in the binding attribute:

<contractRef ref=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request) + "?wsdl"),Response.Output) %>

docRef=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output) %>

xmlns="http://schemas.xmlsoap.org/disco/scl/" />

<soap address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output) %>

xmlns:q1="http://tempuri.org/" binding="q1:HelloWorld" xmlns="http://schemas.xmlsoap.org/disco/soap/" />

<soap address=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output) %>

xmlns:q2="http://tempuri.org/" binding="q2:ServiceSoap12" xmlns="http://schemas.xmlsoap.org/disco/soap/" />

 

5.     In the .wsdl file, make the following similar substitution for the SOAP address that is specified:

<soap:address location=<% SPHttpUtility.AddQuote(SPHttpUtility.HtmlEncode(SPWeb.OriginalBaseUrl(Request)),Response.Output) %> />

 

6.     Rename both files in the respective formats MyCustomWebServicedisco.aspx and MyCustomWebServicewsdl.aspx so that your service is discoverable through Windows SharePoint Services.

**NOTE** you will need to ‘temporarily’ exclude these two files from the project if you wish to run the web service in Visual Studio. (don’t forget to include them again later so they are in Source control)

Package and deploy the Web service files to the _vti_bin directory. (12 hive ISAPI folder)

There are two ways you can deploy the web service to a SharePoint server:

·          Add the class library to the GAC and then manually copy the .asmx and the two .aspx files from the web service project to the ISAPI folder (Local_Drive:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\12\ISAPI)

·          Create a solution package for your class library project which deploys the assembly to the GAC along with the appropriate files to the ISAPI folder.

Being that best practice is to deploy anything on SharePoint via solution packages (WSPs) we will be focussing on the second option. That said, the first option could be used if you wished to test ‘quickly’ in a local environment.

Packaging a web service as SharePoint solution file (WSP)

You have 3 major steps:

 

1.     Build your web service (as per the steps above)

2.     Set up the SharePoint Solution

3.     Add the Solution to SharePoint and Deploy

 

First, get all your necessary files into your Class Library project

1.     Create folders to hold your files – in the Class Library project you created, right click > Add New > Folder, and create:

DeploymentFiles – to hold all the SharePoint solution generation files

ISAPI – to mimic the final location of all your web service files

2.     Move all the web service files into the ISAPI folder from the web service project (.asmx and .aspx)

3.     Create the SharePoint Solution generation files in the DeploymentFiles folder:

- BuildSharePointPackage.ddf – this file tells makecab.exe which files in the solution will be included in the .WSP file and in what structure

 

; *** .ddf file for generating SharePoint solution.

;

.OPTION EXPLICIT ; Generate errors

.set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory

.Set CompressionType=MSZIP;** All files are compressed in cabinet files

.Set UniqueFiles="ON"

.Set Cabinet=on

 

DeploymentFiles\manifest.xml

Bin\debug\CustomWebService.dll ; *Note* that this value should be changed if you set the build configuration to ‘release’.

 

.Set DestinationDir=ISAPI

ISAPI\CustomWebService.asmx

ISAPI\CustomWebServicedisco.aspx

ISAPI\CustomWebServicewsdl.aspx

 

;***End

 

- BuildSharePointPackage.Targets – tells Visual Studio how to call MakeCab.exe to create the WSP file

 

<Project DefaultTargets="BuildSharePointPackage" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup>

<MakeCabPath>"C:\Windows\System32\MAKECAB.EXE"</MakeCabPath>

</PropertyGroup>

<Target Name="BuildSharePointPackage">

<Exec Command="C:\Windows\System32\MAKECAB.EXE /F DeploymentFiles\BuildSharePointPackage.ddf /D CabinetNameTemplate=$(MSBuildProjectName).wsp /D DiskDirectory1=WSPs\$(Configuration) "/>

<Exec Condition=" '$(Configuration)$(Platform)' == 'DebugAnyCPU'"

Command="$(MakeCabPath) /F DeploymentFiles\BuildSharePointPackage.ddf /D CabinetNameTemplate=$(MSBuildProjectName).cab /D DiskDirectory1=WSPs\$(Configuration)"/>

</Target>

/Project>

 

** Please not that the <MakeCabPath> should point to wherever you have MakeCab.exe installed.

 

- manifest.xml – this files tells the WSP what to do with the files when the solution is deployed

 

<?xml version="1.0" encoding="utf-8" ?>

<Solution xmlns="http://schemas.microsoft.com/sharepoint/"

SolutionId="70DA8F86-312E-11DE-9B70-CEFF55D89593"

DeploymentServerType="WebFrontEnd"

ResetWebServer="TRUE">

 

<RootFiles>

<RootFile Location="ISAPI\CustomWebService.asmx"/>

<RootFile Location="ISAPI\CustomWebServicedisco.aspx"/>

<RootFile Location="ISAPI\CustomWebServicewsdl.aspx"/></RootFiles>

<Assemblies>

<Assembly DeploymentTarget="GlobalAssemblyCache" Location="CustomWebService.dll">

</Assembly>

</Assemblies>

</Solution>

 

Lets break this part down, as it is important.

 

- SolutionId – get a new GUID to identify your solution

- <RootFiles> – this tag tells the solution to put files at the root lever, the 12 Hive

- <RootFile> – one tag for each file,the location property tells where in the WSP we are getting the file to place at the Root. B/c we put the the file into the WSP at the ISAPI path, the manifest will also deploy the file to the ISAPI path from the root location.

- <Assembly> – this tag will place the dlls in the appropriate location. The DeploymentTarget set to “GlobalAssemblyCache” will put the dll in the GAC.

4.     Update the .vbproj file to user MakeCab to generate the WSP

- Unload the project and edit the .vbproj file

 

Find the line:

<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />

 

Below there, enter the following lines:

 

<Import Project="DeploymentFiles\BuildSharePointPackage.targets" />

<Target Name="AfterBuild">

<CallTarget Targets="BuildSharepointPackage" Condition=" '$(Configuration)$(Platform)' == 'DebugAnyCPU' " />

</Target>

 

Save and Reload the project.

 

5.     Build the project and you should have WSP files in a WSPs folder in the project. (Make sure you include this folder in your project.)

6.     Add the WSP to SharePoint & Deploy!

 

use stsadm to add the solution to the central admin solution store

 

a.     from the 12 hive/bin, open a command window and run the following command:

 

stsadm –o addsolution –filename [Location of your .wsp file]

 

b.     Then go to Central Administration > Operations > Solution Management

You should see your wsp in there. Click the name and deploy it immediately.

 

Testing the service

Verify that :

·          your dll is in the GAC

·          your .asmx, xxxwsdl.aspx, and xxxdisco.aspx are in the 12 hive/ISAPI folder

·          and that you can navigate now to http://yourserver/_vti_bin/yourservice.asmx

 

From the _vti_bin directory, a Web service offers its functionality to the site that is specified when adding a Web reference for the service.

To verify that your custom Web service is discoverable, navigate to http://MyServer/_vti_bin/MyCustomWebService.asmx

The next step is to test the web service itself by creating an application to consume it.

Creating a Windows Application to Consume the Web Service

After you copy the Web services files to the _vti_bin directory, the next step is to create a Windows Application to consume the Web service.

To create a Windows Application that consumes the Web service

1.     Open Visual Studio, and on the File menu, point to New, and then click Project.

2.     In the New Project dialog box, select Visual Basic, and then select the Windows Application template. (set the framework target to .net 2.0 initially)

3.     Type a name for the application in the Name box, specify a location for the project files in the Location box, and then click OK.

4.     In Solution Explorer, right-click the project, and then click Add Web Reference.

5.     In the address bar of the Add Web Reference browser, type the URL for the site to apply the service to, as follows, and then press ENTER:

http://Server_Name/[sites/][Site_Name/]_vti_bin/MyCustomWebService.asmx

6.     Click Add Reference to download the service contract for the Web service.  (you can change the framework target now if you desire)

7.     Open Form1 in Design view, display the Toolbox, and then drag a button onto the form.

8.     Double-click the Button1 control on Form1 to display the code-behind file in the code editor, and add the following code that calls your custom method.

Dim MyCustomService As New Web_Reference_Folder.MyServiceClass()

MyCustomService.UseDefaultCredentials = True

MessageBox.Show(MyCustomService.HelloWorld())

 

9.     Press F5 to compile and run the project and see a message box that displays "Hello World".

Implementing the Windows SharePoint Services Object Model

Now you are ready to try out types and members of the Windows SharePoint Services object model in the class library of your Web service.

To implement the Windows SharePoint Services object model

1.     Add a reference to the Microsoft.SharePoint assembly. Right-click the class library project in Solution Explorer, click Add Reference, select Windows SharePoint Services, and then click OK.

2.     In your project Service.vb file, you must import the appropriate namespaces in the object model. For example, to use types and members of the Microsoft.SharePoint and Microsoft.SharePoint.Utilities namespaces, add the following directives:

Imports Microsoft.SharePoint

Imports Microsoft.SharePoint.Utilities

 

3.     Rename the method in Service.vb. For example, change the name from HelloWorld to GetSiteListCount.

4.     Add the following code to display the name of the Web site and the number of lists that it contains.

Dim myWebSite As SPWeb = SPContext.Current.Web

Dim lists As SPListCollection = myWebSite.Lists

 

Return myWebSite.Title + " contains " + lists.Count.ToString() +

" lists."


**NOTE**If your code modifies Windows SharePoint Services data in some way, you may need to allow unsafe updates on the Web site, which you can do by setting the AllowUnsafeUpdates property.

 

In addition, there is a known issue in the SharePoint object model when using ServerContext in a web service. See http://social.msdn.microsoft.com/forums/en-US/sharepointdevelopment/thread/d6f1fd35-9aa0-49da-80b6-b3c8530e7c47 for details.



5.     To rebuild the class library DLL and copy it to the GAC, repeat steps 8 and 9 in "To create an ASP.NET Web service."

6.     Reset Internet Information Services (IIS) for changes in the DLL to take effect.

7.     To generate new versions of the .disco and .wsdl files, repeat the steps in "To generate the static discovery and WSDL files," but change the method binding name in the .disco file as appropriate, for example, to GetSiteListCount.

8.     Copy the new versions of the MyCustomWebServicedisco.aspx and MyCustomWebServicewsdl.aspx files to the ISAPI folder.

9.     Open the Windows application you created previously, delete the previous Web service reference for the Hello World example, change the method name as appropriate, and add a new Web reference to the revised Web service.

10.  Press F5 to compile and run the project and see a message box that displays the Web site name and the number of lists that it contains.

 

No comments:

Post a Comment