IIS .asmx requests are routed by default to the WebServiceHandlerFactory assembly, as specified in machine.config. The following excert from machine.config embodies this routing
<httphandlers>
<add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false"></add>
</httphandlers>
Thereby, normal Web service does not require page separation to CS file and asmx file pointing to the source, as VS separation. All your code may be placed in the single asmx file like
<@WebService Language="c#" class="Foo.bar" %>
using System;
using System.Web.Services;
namespace Foo
{
[WebService(Namespace="urn:...")]
public class bar
{
[WebMethod]
SayHello()
{
}
}
At application level you can easely repalce this routing : insert the following in the web.config
.
After this ASP.NET will response to your reguest for asmx page with
"This type of page is not served." message. This message steams from HttpForbiddenHandler that actually formats the HTTP response. How?
HttpForbiddenHandler implements IHttpHandler interface overriding its methos as follow:
public void ProcessRequest(HttpContext context)
{
PerfCounters.IncrementCounter(AppPerfCounter.REQUESTS_NOT_FOUND);
throw new HttpException(403, HttpRuntime.FormatResourceString("Path_forbidden", context.Request.Path));
}
public bool IsReusable
{
get { return true; }
}
(The code restored with help of Lutz Roeder's .NET Reflector -
http://www.aisto.com/roeder/dotnet/)
If you want to implement your own http handlers - do the same : inherit from IHttpHandler and provide the impelemantation for ProcessRequest() method.
The ultimate goal of HttpHanlder's ProcessRequest() method is to render the markup whatever the way it desired. It is possible to achive this goal by "manual" rendering like this:
public void ProcessRequest(HttpContext context)
{
context.Response("Hello");
}
It is possible to render the markup by programmatically creating Web controls and using RenderControl() method like this:
public void ProcessRequest(HttpContext context)
{
Panel p = new Panel();
Label lbl = new Label();
lbl.Text = "Hello";
p.Controls.Add(lbl);
// render the Panel Control
StringWriter sw = new StringWriter();
HtmlTextWriter writer = new HtmlTextWriter();
p.RenderControl();
// Emit the rendered HTML
context.Response.Write(sw.ToString());
}
Where HTTP handlers may be useful in responding to requests for specific resources, a handler factory makes it possible to intercept a request, perform some pre-processing on the request, and then following a factory pattern, create the handler for the resource.
For example, handler factory may log the client's address into server-based DB and after that follow Page-handler request using the fact, that System.Web.UI.Page class actually implements IHttpHandler and as so may serve as normal Http handler for aspx pages.
For example : assuming the following web.config configuration
consider the code
public class HitLogger : IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, string requestType,
string url, string pathTranslated)
{
Object handler = null;
try
{
//
// Perform DB Logging here...
//
string filename = url.Substring(url.LastIndexOf('/')+1);
string ext = filename.Substring(filename.LastIndexOf('.')+1);
if( ext == "aspx" )
{
return System.Web.UI.PageParser.GetCompiledPageInstance
(url, pathTranslated, context);
}
else if( ext == "asmx" )
{
System.Web.Services.Protocols.WebServiceHandlerFactory fact =
new System.Web.Services.Protocols.WebServiceHandlerFactory();
}
else
{
throw new HttpException("Unable to process extension *." + ext);
}
}
catch(Exception ex)
{
throw new HttpException("HitLogger", ex);
}
return (IHttpHandler)handler;
}
}