Introduction
Recently I worked on a plug-in based system, and one of the approaches of this system was to allow each plug-in (dll) to have the capability to host web services. I had never heard about such task before, I made a search on the Web and didn't find anything about that, so I decided to think and fortunately I came with a pretty good solution.
The HTTP Handler for .asmx
The first thing I did was to open the Web.Config file located in %windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG and look at the following entry inside <httpHandlers>:
<add path="*.asmx" verb="*" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="False" />
Using Reflector, I found that the WebServiceHandlerFactory builds and returns an IHttpHandler in the GetHandler of the IHttpHandlerFactory interface, that was all I needed to know.
Creating the WebServiceBase class
So here it is what I did:
I created a new Class Library project and in this project, a class named WebServiceBase
, this class could be anywhere, since it's an abstract class that will be the base class for the web services. The definition of this class is below:
Imports System.Web
Imports System.Web.Services
Imports System.Web.Services.Protocols
Imports System.Reflection
Public MustInherit Class WebServiceBase
Inherits WebService
Implements IHttpHandlerFactory
Private Shared wshf As New WebServiceHandlerFactory
Private Shared coreGetHandlerMethod As MethodInfo = GetType(WebServiceHandlerFactory).GetMethod("CoreGetHandler", _
BindingFlags.Instance Or BindingFlags.NonPublic)
Public Function GetHandler(ByVal context As System.Web.HttpContext, ByVal requestType As String, ByVal url As String, _
ByVal pathTranslated As String) As System.Web.IHttpHandler Implements System.Web.IHttpHandlerFactory.GetHandler
Return DirectCast(coreGetHandlerMethod.Invoke(wshf, New Object() {Me.GetType, context, context.Request, context.Response}), IHttpHandler)
End Function
Public Sub ReleaseHandler(ByVal handler As System.Web.IHttpHandler) Implements System.Web.IHttpHandlerFactory.ReleaseHandler
End Sub
End Class
Note the Reflection hack to call the CoreGetHandler method from WebServiceHandlerFactory, this is needed because this method is declared as Friend, and this method is the one that actually creates the IHttpHandler instance.
It's almost done! All you have to do now is create the web services, which will be classes that inherit from this one, so here is an example:
Imports System.Web.Services
<WebService(Namespace:="http://tempuri2.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
Public Class WSTest
Inherits WebServiceBase
<WebMethod()> _
Public Function HelloWorld() As String
Return "Hello World"
End Function
End Class
Remember that everything above is in a Class Library, so compile this project, create a new web site and add this project as a reference.
This Class Library project I called WSLibrary, and as the WSTest
is nothing more than a HttpHandler
, you should add a new entry to your web.config for each Web Service you created, in this example above, I added:
<httpHandlers>
<add path="WSTest.asmx" verb="*" type="WSLibrary.WSTest" validate="false"/>
</httpHandlers>
Now, every time a request to WSTest.asmx is made, it will be handled by the WSLibrary project. It can be changed very easily to suit your needs.
Conclusion
This approach worked very well for the project I was working on. I'm sure there are more things that could be made to make it even better, like you can make a way that only one entry in the httpHandlers
is necessary for as many web services as you have.