Introduction
While out-of-the-box support for web services in .Net is excellent, there are a few areas that we thought could use some improvement.
Server-side issues
One thing that we didn't like much is that we had to have all those .asmx file lying around when all they do is specify which class to instantiate to handle web service requests.
Second, we also wanted to be able to use Spring's core DI capabilities to inject dependencies into our web service instances. In most cases, web service will rely on other objects, such as a DAO for example, so being able to configure which DAO implementation to use is very useful.
Last, but not least, we didn't like the fact that creating a web service is an implementation task. Our belief is that most services are best implemented as normal classes that use coarse grained service interface, and the decision on whether particular service should be exposed as a remote object, web service, or even enterprise (COM+) component, should be a matter of configuration, not implementation.
Client-side issues
On the client side, the main objection we have is that client code becomes tied to a proxy class, 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", your code will be less flexible and it will be impossible to plug in different service implementation in case you decide to use a new and improved web service implementation or a local service instead of a web service.
The goal for Spring is to enable generation of the client-side proxies that implement a specific service interface.
Solution
Removing the need for .asmx files
Unlike web pages, which use .aspx file to store presentation code and code behind class for the logic, web services are completely implemented within the code behind class. That means that .asmx file serves no useful purpose, and as such it shouldn't be necessary nor required.
Spring.Net allows you to expose existing web services easily by registering a custom implementation of WebServiceHandlerFactory class and by creating standard Spring object definition for the service.
Let's assume that you have a simple web service such as this one:
namespace MyComany.MyApp.Services
{
[WebService(Namespace="http:)]
public class HelloWorldService
{
[WebMethod]
public string HelloWorld()
{
return "Hello World!";
}
}
}
Again, this is just a standard class that has methods decorated with WebMethod attribute and optionally with a class-level WebService attribute. You can create it within VS.Net just like you would create any other class, in order to avoid generating .asmx and .resx files for it.
All you need to do in order to publish this service is:
1. Register Spring.Web.Services.WebServiceFactoryHandler as HTTP handler for *.asmx requests within your Web.config file
<system.web>
<httpHandlers>
<add verb="*" path="*.asmx" type="Spring.Web.Services.WebServiceHandlerFactory, Spring.Web"/>
</httpHandlers>
...
</system.web>
Of course, you can register any other extension as well, but there is no need for it as Spring's handler factory will behave exactly the same as a standard one if it doesn't find the object definition for the specified service name. In that case the handler will simply look for a .asmx page.
2. Create object definition for your web service
<object name="HelloWorld.asmx" type="MyComany.MyApp.Services.HelloWorldService, MyAssembly" abstract="true"/>
You don't need to make it abstract, but you probably should 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 needs to provide is the type of the service class, which can easily be retrieved from the definition above even if it is abstract.
That's pretty much it – you can access this web service using value specified in the name attribute of the object definition as a service name:
Injecting dependencies into web services
For arguments sake, let's say that we want to change our HelloWorld method and make the message that it returns 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 feel kinda awkward to use Spring Dependency Injection throughout the application to configure objects, but to have to use locator within web services.
Ideally, you should be able to define a property for the message within your web service class and have Spring inject the message value into it:
namespace MyApp.Services
{
public interface IHelloWorld
{
string HelloWorld();
}
[WebService(Namespace="http:)]
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 DI usage in this case is that Spring doesn't control instantiation of your web service. This happens deep in the internals of .Net framework so it's almost impossible to plug in the code that will perform the configuration.
The solution is to create dynamic server-side proxy that will wrap your web service and configure it. That way, .Net framework gets a reference to a proxy type from Spring and instantiates it. The proxy then asks Spring applicatin context for the actual web service instance that will process requests.
In order for this to work there is one extra step that you need to make – export the web service explicitly using Spring.Web.Services.WebServiceExporter class and of course, configure the Message property for your service:
<object id="HelloWorld" type="MyApp.Services.HelloWorldService, MyApp">
<property name="Message">
<value>Hello, World!</value>
</property>
</object>
<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName"><value>HelloWorld</value></property>
</object>
The exporter copies the existing web service and method attribute values to the proxy implementation, if any are defined. However, existing values can be overriden by setting properties on the WebServiceExporter.
 | Interface Requirement
In order to support some advanced usage scenarios, such as the ability to expose AOP proxy as a web service, thus allowing addition of AOP advices to web service methods, Spring.NET requires 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 class. |
Exposing PONOs 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 useless – 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 WebService attribute and WebMethod attribute declarations from the service implementation, and what we are left with is a plain old .Net object. 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 not the ideal solution. You would loose information that optional WebService and WebMethod properties provide, such as service namespace, description, transaction mode, etc. One way to keep those values is to leave them within the service class and proxy generator will simply copy them to the proxy class instead of creating empty ones, but that kinda defeats the purpose.
Second, better way, is to set all necessary values within the service exporter:
<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
<property name="TargetName"><value>HelloWorld</value></property>
<property name="Namespace"><value>http://myCompany/services</value></property>
<property name="Description"><value>My exported HelloWorld web service</value></property>
<property name="Methods">
<dictionary>
<entry key="HelloWorld">
<object type="System.Web.Services.WebMethodAttribute, System.Web.Services">
<property name="Description">
<value>My Spring-configured HelloWorld method.</value>
</property>
<property name="MessageName">
<value>ZdravoSvete</value>
</property>
</object>
</entry>
</dictionary>
</property>
</object>
// or, once configuration improvements are implemented, much simpler
<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 necessary attributes as appropriate. This accomplishes the same goal while moving web service metadata from implementation class to configuration, which allows us to export any class as a web service.
You can also export only certain interfaces that service class implements by setting Interfaces property of the WebServiceExporter:
<object id="HelloWorldExporter" type="Spring.Web.Services.WebServiceExporter, Spring.Web">
...
<property name="Interfaces">
<list>
<value>MyApp.Services.IHelloWorld, MyApp</value>
</list>
</property>
...
</object>
 | 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. |
Exporting AOP Proxy as a Web Service
It is very useful to be able to export AOP proxy as a web service. For example, you could have a service that's wrapped with an AOP proxy that you want to access both locally and remotely as a web service. Local client would simply obtain reference to an AOP proxy directly, but remote client needs to obtain 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.
This is actually fairly straight forward – because AOP proxy is an object just like any other object, all you need to do is set web service exporter's TargetName property to an ID of the AOP proxy:
<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"/>
<property name="Interfaces">
<list>
<value>MyApp.Services.IMyService, MyApp</value>
</list>
</property>
</object>
That's it – every call to your web service methods will be intercepted by the AOP proxy, which will apply debug and timer advices to it.
Client-side Proxy Creation
The problem with 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 implementation at a later date without modifying and recompiling the client.
Spring provides a simple IFactoryObject implementation that will generate "proxy for proxy". Basically, Spring.Web.Services.WebServiceProxyfactory class will create a proxy for the VS.Net/WSDL generated proxy that implements specified service interface.
For example, we could define IHelloWorld interface for our web service above that looks like this:
namespace MyCompany.Services
{
public interface IHelloWorld
{
string HelloWorld();
}
}
In order to be able to reference web service through this interface, you need to add following definition to your client's application context (assuming that VS.Net/WSDL generated proxy class is called MyCompany.WebServices.HelloWorld):
<object id="HelloWorld" type="Spring.Web.Services.WebServiceProxyFactory, Spring.Services">
<property name="ProxyClass"><value>MyCompany.WebServices.HelloWorld, MyClientApp</value></property>
<property name="ServiceInterface"><value>MyCompany.Services.IHelloWorld, MyServices</value></property>
</object>
What is important to notice is that the actual web service does not have to implement the same IHelloWorld interface – as long as matching methods exist, Spring will be able to create a proxy and delegate calls. If a matching method cannot be found, Spring 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 also implements business interface, especially if you plan on exporting it using Spring.NET WebServiceExporter, which requires interface in order to work.