Chapter 29. Web Services

29.1. Introduction

While the out-of-the-box support for web services in .NET is excellent, there are a few areas that the Spring.NET thought could use some improvement. Spring adds the ability to perform dependency injection on standard asmx web services. Spring's .NET Web Services support also allows you to export a 'plain CLR object' as a .NET web service By "plain CLR object" we mean classes that do not contain infrastructure specific attributes, such as WebMethod. On the server side, Spring's .NET web service exporters will automatically create a proxy that adds web service attributes. On the client side you can use Spring IoC container to configure a client side proxy that you generated with standard command line tools. Additionally, Spring provides the functionality to create the web service proxy dynamically at runtime (much like running the command line tools but at runtime and without some of the tools quirks) and use dependency injection to configure the resulting proxy class. On both the server and client side, you can apply AOP advice to add behavior such as logging, exception handling, etc. that is not easily encapsulated within an inheritance hierarchy across the application.

29.2. Server-side

One thing that the Spring.NET team didn't like much is that we had to have all these .asmx files lying around when all said files did was specify which class to instantiate to handle web service requests.

Second, the Spring.NET team also wanted to be able to use the Spring.NET IoC container to inject dependencies into our web service instances. Typically, a web service will rely on other objects, service objects for example, so being able to configure which service object implementation to use is very useful.

Last, but not least, the Spring.NET team did not like the fact that creating a web service is an implementation task. Most (although not all) services are best implemented as normal classes that use coarse-grained service interfaces, and the decision as to whether a particular service should be exposed as a remote object, web service, or even an enterprise (COM+) component, should only be a matter of configuration, and not implementation.

An example using the web service exporter can be found in quickstart example named 'calculator'. More information can be found here 'Web Services example'.

29.2.1. Removing the need for .asmx files

Unlike web pages, which use .aspx files to store presentation code, and code-behind classes for the logic, web services are completely implemented within the code-behind class. This means that .asmx files serve no useful purpose, and as such they should neither be necessary nor indeed required at all.

Spring.NET allows application developers to expose existing web services easily by registering a custom implementation of the WebServiceHandlerFactory class and by creating a standard Spring.NET object definition for the service.

By way of an example, consider the following web service...

namespace MyComany.MyApp.Services
{
    [WebService(Namespace="http://myCompany/services")]
    public class HelloWorldService
    {
        [WebMethod]
        public string HelloWorld()
        {
            return "Hello World!";
        }
    }
}
      

This is just a standard class that has methods decorated with the WebMethod attribute and (at the class-level) the WebService attribute. Application developers can create this web service within Visual Studio just like any other class.

All that one need to do in order to publish this web service is:

1. Register the Spring.Web.Services.WebServiceFactoryHandler as the HTTP handler for *.asmx requests within one's web.config file.

<system.web>
    <httpHandlers>
        <add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
    </httpHandlers>
</system.web>
      

Of course, one can register any other extension as well, but typically there is no need as Spring.NET's handler factory will behave exactly the same as a standard handler factory if said handler factory cannot find the object definition for the specified service name. In that case the handler factory will simply look for an .asmx file.

If you are using IIS7 the following configuration is needed

<system.webServer>
  <validation validateIntegratedModeConfiguration="false"/>
  <handlers>
    <add name="SpringWebServiceSupport" verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
  </handlers>
</system.webServer>

2. Create an object definition for one's web service.

<object name="HelloWorld" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/>

Note that one is not absolutely required to make the web service object definition abstract (via the abstract="true" attribute), but this is a recommended best practice in order to avoid creating an unnecessary instance of the service. Because the .NET infrastructure creates instances of the target service object internally for each request, all Spring.NET needs to provide is the System.Type of the service class, which can be retrieved from the object definition even if it is marked as abstract.

That's pretty much it as we can access this web service using the value specified for the name attribute of the object definition as the service name:

http://localhost/MyWebApp/HelloWorld.asmx

29.2.2. Injecting dependencies into web services

For arguments sake, let's say that we want to change the implementation of the HelloWorld method to make the returned message configurable.

One way to do it would be to use some kind of message locator to retrieve an appropriate message, but that locator needs to implemented. Also, it would certainly be an odd architecture that used dependency injection throughout the application to configure objects, but that resorted to the service locator approach when dealing with web services.

Ideally, one should be able to define a property for the message within one's web service class and have Spring.NET inject the message value into it:

namespace MyApp.Services
{
    public interface IHelloWorld
    {
        string HelloWorld();
    }

    [WebService(Namespace="http://myCompany/services")]
    public class HelloWorldService : IHelloWorld
    {
        private string message;
        public string Message
        {
            set { message = value; }
        }
    
        [WebMethod]
        public string HelloWorld()
        {
            return this.message;
        }
    }
}
      

The problem with standard Spring.NET DI usage in this case is that Spring.NET does not control the instantiation of the web service. This happens deep in the internals of the .NET framework, thus making it quite difficult to plug in the code that will perform the configuration.

The solution is to create a dynamic server-side proxy that will wrap the web service and configure it. That way, the .NET framework gets a reference to a proxy type from Spring.NET and instantiates it. The proxy then asks a Spring.NET application context for the actual web service instance that will process requests.

This proxying requires that one export the web service explicitly using the Spring.Web.Services.WebServiceExporter class; in the specific case of this example, one must also not forget to configure the Message property for said service:

<object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp">
    <property name="Message" value="Hello, World!"/>
</object>

<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
    <property name="TargetName" value="HelloWorld"/>
</object>
      

The WebServiceExporter copies the existing web service and method attribute values to the proxy implementation (if indeed any are defined). Please note however that existing values can be overridden by setting properties on the WebServiceExporter.

[Tip]Interface Requirements

In order to support some advanced usage scenarios, such as the ability to expose an AOP proxy as a web service (allowing the addition of AOP advices to web service methods), Spring.NET requires those objects that need to be exported as web services to implement a (service) interface.

Only methods that belong to an interface will be exported by the WebServiceExporter.

29.2.3. Exposing POCOs as Web Services

Now that we are generating a server-side proxy for the service, there is really no need for it to have all the attributes that web services need to have, such as WebMethod. Because .NET infrastructure code never really sees the "real" service, those attributes are redundant as the proxy needs to have them on its methods, because that's what .NET deals with, but they are not necessary on the target service's methods.

This means that we can safely remove the WebService and WebMethod attribute declarations from the service implementation, and what we are left with is a plain old CLR object (a POCO). The example above would still work, because the proxy generator will automatically add WebMethod attributes to all methods of the exported interfaces.

However, that is still not the ideal solution. You would lose information that the optional WebService and WebMethod attributes provide, such as service namespace, description, transaction mode, etc. One way to keep those values is to leave them within the service class and the proxy generator will simply copy them to the proxy class instead of creating empty ones, but that really does defeat the purpose.

To add specific attributes to the exported web service, you can set all the necessary values within the definition of the service exporter, like so...

<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
    <property name="TargetName" value="HelloWorld"/>
    <property name="Namespace" value="http://myCompany/services"/>
    <property name="Description" value="My exported HelloWorld web service"/>
    <property name="MemberAttributes">
        <dictionary>
            <entry key="HelloWorld">
                <object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
                    <property name="Description" value="My Spring-configured HelloWorld method."/>
                    <property name="MessageName" value="ZdravoSvete"/>
                </object>
            </entry>
        </dictionary>
    </property>
</object>

// or, once configuration improvements are implemented...
<web:service targetName="HelloWorld" namespace="http://myCompany/services">
    <description>My exported HelloWorld web service.</description>
    <methods>
        <method name="HelloWorld" messageName="ZdravoSvete">
            <description>My Spring-configured HelloWorld method.</description>
        </method>
    </methods>
</web:service>
      

Based on the configuration above, Spring.NET will generate a web service proxy for all the interfaces implemented by a target and add attributes as necessary. This accomplishes the same goal while at the same time moving web service metadata from implementation class to configuration, which allows one to export pretty much any class as a web service.

The WebServiceExporter also has a TypeAttributes IList property for applying attributes at the type level.

[Note]Note

The attribute to confirms to the WSI basic profile 1.1 is not added by default. This will be added in a future release. In the meantime use the TypeAttributes IList property to add [WebServiceBinding(ConformsTo=WsiProfiles.BasicProfile1_1)] to the generated proxy.

One can also export only certain interfaces that a service class implements by setting the Interfaces property of the WebServiceExporter.

[Warning]Distributed Objects Warning

Distributed Objects Warning

Just because you can export any object as a web service, doesn't mean that you should. Distributed computing principles still apply and you need to make sure that your services are not chatty and that arguments and return values are Serializable.

You still need to exercise common sense when deciding whether to use web services (or remoting in general) at all, or if local service objects are all you need.

29.2.4. Exporting an AOP Proxy as a Web Service

It is often useful to be able to export an AOP proxy as a web service. For example, consider the case where you have a service that is wrapped with an AOP proxy that you want to access both locally and remotely (as a web service). The local client would simply obtain a reference to an AOP proxy directly, but any remote client needs to obtain a reference to an exported web service proxy, that delegates calls to an AOP proxy, that in turn delegates them to a target object while applying any configured AOP advice.

Effecting this setup is actually fairly straightforward; because an AOP proxy is an object just like any other object, all you need to do is set the WebServiceExporter's TargetName property to the id (or indeed the name or alias) of the AOP proxy. The following code snippets show how to do this...

<object id="DebugAdvice" type="MyApp.AOP.DebugAdvice, MyApp"/>

<object id="TimerAdvice" type="MyApp.AOP.TimerAdvice, MyApp"/>

<object id="MyService" type="MyApp.Services.MyService, MyApp"/>

<object id="MyServiceProxy" type="Spring.Aop.Framework.ProxyFactoryObject, Spring.Aop">
    <property name="TargetName" value="MyService"/> 
    <property name="IsSingleton" value="true"/>
    <property name="InterceptorNames">
        <list>
            <value>DebugAdvice</value>
            <value>TimerAdvice</value>
       </list>
    </property>
</object>

<object id="MyServiceExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
    <property name="TargetName" value="MyServiceProxy"/>
    <property name="Name" value="MyService"/>
    <property name="Namespace" value="http://myApp/webservices"/>
    <property name="Description" value="My web service"/>
</object> 
      

That's it as every call to the methods of the exported web service will be intercepted by the target AOP proxy, which in turn will apply the configured debugging and timing advice to it.

29.3. Client-side

On the client side, the main objection the Spring.NET team has is that client code becomes tied to a proxy class, and not to a service interface. Unless you make the proxy class implement the service interface manually, as described by Juval Lowy in his book "Programming .NET Components", application code will be less flexible and it becomes very difficult to plug in different service implementation in the case when one decides to use a new and improved web service implementation or a local service instead of a web service.

The goal for Spring.NET's web services support is to enable the easy generation of client-side proxies that implement a specific service interface.

29.3.1. Using VS.NET generated proxy

The problem with the web-service proxy classes that are generated by VS.NET or the WSDL command line utility is that they don't implement a service interface. This tightly couples client code with web services and makes it impossible to change the implementation at a later date without modifying and recompiling the client.

Spring.NET provides a simple IFactoryObject implementation that will generate a "proxy for proxy" (however obtuse that may sound). Basically, the Spring.Web.Services.WebServiceProxyFactory class will create a proxy for the VS.NET- / WSDL-generated proxy that implements a specified service interface (thus solving the problem with the web-service proxy classes mentioned in the preceding paragraph).

At this point, an example may well be more illustrative in conveying what is happening; consider the following interface definition that we wish to expose as a web service...

namespace MyCompany.Services
{
    public interface IHelloWorld 
    {
        string HelloWorld();
    }
} 
      

In order to be able to reference a web service endpoint through this interface, you need to add a definition similar to the example shown below to your client's application context:

<object id="HelloWorld" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
    <property name="ProxyType" value="MyCompany.WebServices.HelloWorld, MyClientApp"/>
    <property name="ServiceInterface" value="MyCompany.Services.IHelloWorld, MyServices"/>
</object>
      

What is important to notice is that the underlying implementation class for the web service does not have to implement the same IHelloWorld service interface... so long as matching methods with compliant signatures exist (a kind of duck typing), Spring.NET will be able to create a proxy and delegate method calls appropriately. If a matching method cannot be found, the Spring.NET infrastructure code will throw an exception.

That said, if you control both the client and the server it is probably a good idea to make sure that the web service class on the server implements the service interface, especially if you plan on exporting it using Spring.NET's WebServiceExporter, which requires an interface in order to work.

29.3.2. Generating proxies dynamically

The WebServiceProxyFactory can also dynamically generate a web-service proxy. The XML object definition for this factory object is shown below

        <object id="calculatorService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
          <property name="ServiceUri" value="http://myServer/Calculator/calculatorService.asmx"/>
          <!--<property name="ServiceUri" value="file://~/calculatorService.wsdl"/>-->
          <property name="ServiceInterface" value="Spring.Calculator.Interfaces.IAdvancedCalculator, Spring.Calculator.Contract"/>
          <!-- Dependency injection on Factory's product : the proxy instance of type SoapHttpClientProtocol -->
          <property name="ProductTemplate">
            <object>
              <property name="Timeout" value="10000" /> <!-- 10s -->
            </object>
          </property>
        </object>
        
      

One use-case where this proxy is very useful is when dealing with typed data sets through a web service. Leaving the pros and cons of this approach aside, the current behavior of the proxy generator in .NET is to create wrapper types for the typed dataset. This not only pollutes the solution with extraneous classes but also results in multiple wrapper types being created, one for each web service that uses the typed dataset. This can quickly get confusing. The proxy created by Spring allows you to reference you typed datasets directly, avoiding the above mentioned issues.

29.3.3. Configuring the proxy instance

The WebServiceProxyFactory also implements the interface, Spring.Objects.Factory.IConfigurableFactoryObject, allowing to specify configuration for the product that the WebServiceProxyFactory creates. This is done by specifying the ProductTemplate property. This is particularly useful for securing the web service. An example is shown below.

 <object id="PublicarAltasWebService" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
    <property name="ProxyType" value="My.WebService" />
    <property name="ServiceInterface" value="My.IWebServiceInterface" />
    <property name="ProductTemplate"> 
      <object>
        <!-- Configure the web service URL -->
        <property name="Url" value="https://localhost/MyApp/webservice.jws" />
         <!-- Configure the Username and password for the web service --> 
        <property name="Credentials">
          <object type="System.Net.NetworkCredential, System">
            <property name="UserName" value="user"/>
            <property name="Password" value="password"/>
          </object>
        </property>
        <!-- Configure client certificate for the web service --> 
        <property name="ClientCertificates">
          <list>
            <object id="MyCertificate" type="System.Security.Cryptography.X509Certificates.X509Certificate2, System">
              <constructor-arg name="fileName" value="Certificate.p12" />
              <constructor-arg name="password" value="notgoingtotellyou" />
            </object>
          </list>
        </property>
      </object>
    </property>
</object>
      

For an example of how using SOAP headers for authentication using the WebServiceExporter and WebServiceProxyFactory, refer to this solution on our wiki.