Friday, November 28, 2008

How to use .asmx extension to handle WCF requests?

For backward compatibility, WCF supports easy server-side migration from ASMX to WCF. This is good news for existing ASMX customers. Generally, the clients of ASMX may have server Urls hard-coded with the extension “.asmx”. For example, here is a sample Url:

http://www.myserver.com/foo/MyService.asmx

After installing WCF on the box, you can let WCF to handle the same client requests. Here are the steps about how to achieve this:

Replace BuildProvider for ASMX

You need to use the WCF service build provider for .asmx extension by seting the following in the web.config file of your virtual application:

<system.web>

<compilation>

<buildProviders>

<remove extension=".asmx" />

<add extension=".asmx" type="System.ServiceModel.Activation.ServiceBuildProvider, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />

buildProviders>

compilation>

system.web>

When WCF is running in the Mixed Transport mode (instead of AspNetCompatibility mode), WCF would intercept the request based on the BuildProvider information for the extension.

Replace HttpHandler for ASMX

If you are running WCF in AspNetCompatibility mode, you would also need to change the HttpHandler s for .asmx to the WCF HttpHandler. On IIS6, you just need to have something as following in your config:

<system.web>

<httpHandlers>

<remove path="*.asmx" verb="*">remove>

<add path="*.asmx" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

validate="false" />

httpHandlers>

system.web>

On IIS7, things are different. IIS7 introduces two different HTTP pipelines: Integrated and Classic. The latter works similar as IIS6 in the way that all of the managed requests go through the ASP.NET ISAPI filter. When this happens, the HttpModules and HttpHandlers defined in web.config files would be invoked for the corresponding requests.

When the virtual application is running in the Integrated mode, however, the modules and handlers defined in web.config files under the section are totally ignored. All of the requests are handled straightforward by IIS7. So you need to make sure that you have integrated handlers defined for the corresponding extensions. We can set the following in the web.config of the virtual application to achieve this:

<system.webServer>

<handlers>

<remove name="WebServiceHandlerFactory-Integrated"/>

<add name="MyNewAsmxHandler" path="*.asmx" verb="*"type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"preCondition="integratedMode,runtimeVersionv2.0" />

handlers>

<validation validateIntegratedModeConfiguration="false" />

system.webServer>

The section is actually rooted in %windir%\system32\inetsrv\config\applicationhost.config instead of in the root web.config in %windir%\Microsoft.Net\Framework{64}\v2.0.50727\config. The element takes the “name” as the key instead of the “path” as we did to the section. You must specify “validateIntegratedModeConfiguration” to false to make IIS happy.

Correct SOAP Actions

Once you configured above settings, you can create a .asmx file which looks exactly as a .svc file. For example, you can have the following content:

<% @ServiceHost language = "c#" Factory="HelloWorld.SimpleServiceHostFactory" %>

Now you can browse your service with an .asmx extension. At this point, all requests will go through happily into WCF channel stack.

One last thing that you need to be aware is that you need to make sure your WCF service is backward compatible to ASMX so that ASMX clients can communicate with it. Basically you need to use the interoperable binding such as BasicHttpBinding. And you need to make sure the request/response SOAP actions are expected for ASMX clients. Here is an example:

[ServiceContract(Name = "RequestReplyService", Namespace ="http://myoldasmxworld.org/")]

public interface IRequestReplySyncServiceAsmx

{

[OperationContract(IsOneWay = false, Action ="http://myoldasmxworld.org/HelloRequestReply")]

string HelloRequestReply(string text);

}

It is compatible to the following ASMX web service:

[WebService(Name = "MyAsmxService", Namespace ="http://myoldasmxworld.org/")]

public class AsmxRequestReplyService

{

[WebMethod]

public string HelloRequestReply(string text)

{

// Implementation

}

}

2 comments:

AMB said...

This was extremely helpful in managing a client compatibility crisis that I ran into. Thanks very much for posting it!

Regards,
Aaron

saurabh said...

extremely informative...we had a service in asmx and was used by numerous clients ( some of whom we dont have track of). We wanted to change to WCF but it should not have affected our client in ANY WAY..this post shows me the path..thanks :)..