ASMX web services routing in ASP.NET Web Forms - c#

NOTE: There is no MVC in this code. Pure old Web Forms and .asmx Web Service.
I have inherited a large scale ASP.NET Web Forms & Web Service (.asmx) application at my new company.
Due to some need I am trying to do URL Routing for all Web Forms, which I was successfully able to do.
Now for .asmx, routes.MapPageRoute does not work. Based on the below article, I created an IRouteHandler class. Here's how the code looks:
using System;
using System.Web;
using System.Web.Routing;
using System.Web.Services.Protocols;
using System.Collections.Generic;
public class ServiceRouteHandler : IRouteHandler
{
private readonly string _virtualPath;
private readonly WebServiceHandlerFactory _handlerFactory = new WebServiceHandlerFactory();
public ServiceRouteHandler(string virtualPath)
{
if (virtualPath == null)
throw new ArgumentNullException("virtualPath");
if (!virtualPath.StartsWith("~/"))
throw new ArgumentException("Virtual path must start with ~/", "virtualPath");
_virtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Note: can't pass requestContext.HttpContext as the first parameter because that's
// type HttpContextBase, while GetHandler wants HttpContext.
return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath, requestContext.HttpContext.Server.MapPath(_virtualPath));
}
}
http://mikeoncode.blogspot.in/2014/09/aspnet-web-forms-routing-for-web.html
Now when I do routing via Global.asax, it work for the root documentation file but does not work with the Web Methods inside my .asmx files.
routes.Add("myservice", new System.Web.Routing.Route("service/sDxcdfG3SC", new System.Web.Routing.RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/service/myoriginal.asmx")));
routes.MapPageRoute("", "service/sDxcdfG3SC", "~/service/myoriginal.asmx");
Goal
I would like to map an .asmx Web Method URL such as www.website.com/service/myservice.asmx/fetchdata to a URL with obscured names in it like www.website.com/service/ldfdsfsdf/dsd3dfd3d using .NET Routing.
How can this be done?

It is slightly more tricky to do this with routing than in the article you posted because you don't want the incoming URL to have a query string parameter and it looks like the WebServiceHandler won't call the method without an ?op=Method parameter.
So, there are a few parts to this:
A custom route (ServiceRoute) to do URL rewriting to add the ?op=Method parameter
An IRouteHandler to wrap the WebServiceHandlerFactory that calls the web service.
A set of extension methods to make registration easy.
ServiceRoute
public class ServiceRoute : Route
{
public ServiceRoute(string url, string virtualPath, RouteValueDictionary defaults, RouteValueDictionary constraints)
: base(url, defaults, constraints, new ServiceRouteHandler(virtualPath))
{
this.VirtualPath = virtualPath;
}
public string VirtualPath { get; private set; }
public override RouteData GetRouteData(HttpContextBase httpContext)
{
// Run a test to see if the URL and constraints don't match
// (will be null) and reject the request if they don't.
if (base.GetRouteData(httpContext) == null)
return null;
// Use URL rewriting to fake the query string for the ASMX
httpContext.RewritePath(this.VirtualPath);
return base.GetRouteData(httpContext);
}
}
ServiceHandler
public class ServiceRouteHandler : IRouteHandler
{
private readonly string virtualPath;
private readonly WebServiceHandlerFactory handlerFactory = new WebServiceHandlerFactory();
public ServiceRouteHandler(string virtualPath)
{
if (virtualPath == null)
throw new ArgumentNullException(nameof(virtualPath));
if (!virtualPath.StartsWith("~/"))
throw new ArgumentException("Virtual path must start with ~/", "virtualPath");
this.virtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Strip the query string (if any) off of the file path
string filePath = virtualPath;
int qIndex = filePath.IndexOf('?');
if (qIndex >= 0)
filePath = filePath.Substring(0, qIndex);
// Note: can't pass requestContext.HttpContext as the first
// parameter because that's type HttpContextBase, while
// GetHandler expects HttpContext.
return handlerFactory.GetHandler(
HttpContext.Current,
requestContext.HttpContext.Request.HttpMethod,
virtualPath,
requestContext.HttpContext.Server.MapPath(filePath));
}
}
RouteCollectionExtensions
public static class RouteCollectionExtensions
{
public static void MapServiceRoutes(
this RouteCollection routes,
Dictionary<string, string> urlToVirtualPathMap,
object defaults = null,
object constraints = null)
{
foreach (var kvp in urlToVirtualPathMap)
MapServiceRoute(routes, null, kvp.Key, kvp.Value, defaults, constraints);
}
public static Route MapServiceRoute(
this RouteCollection routes,
string url,
string virtualPath,
object defaults = null,
object constraints = null)
{
return MapServiceRoute(routes, null, url, virtualPath, defaults, constraints);
}
public static Route MapServiceRoute(
this RouteCollection routes,
string routeName,
string url,
string virtualPath,
object defaults = null,
object constraints = null)
{
if (routes == null)
throw new ArgumentNullException("routes");
Route route = new ServiceRoute(
url: url,
virtualPath: virtualPath,
defaults: new RouteValueDictionary(defaults) { { "controller", null }, { "action", null } },
constraints: new RouteValueDictionary(constraints)
);
routes.Add(routeName, route);
return route;
}
}
Usage
You can either use MapServiceRoute to add the routes one at a time (with an optional name):
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
routes.MapServiceRoute("AddRoute", "service/ldfdsfsdf/dsd3dfd3d", "~/service/myoriginal.asmx?op=Add");
routes.MapServiceRoute("SubtractRoute", "service/ldfdsfsdf/dsd3dfd3g", "~/service/myoriginal.asmx?op=Subtract");
routes.MapServiceRoute("MultiplyRoute", "service/ldfdsfsdf/dsd3dfd3k", "~/service/myoriginal.asmx?op=Multiply");
routes.MapServiceRoute("DivideRoute", "service/ldfdsfsdf/dsd3dfd3v", "~/service/myoriginal.asmx?op=Divide");
}
}
Alternatively, you can call MapServiceRoutes to map a batch of your web service routes at once:
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
var settings = new FriendlyUrlSettings();
settings.AutoRedirectMode = RedirectMode.Permanent;
routes.EnableFriendlyUrls(settings);
routes.MapServiceRoutes(new Dictionary<string, string>
{
{ "service/ldfdsfsdf/dsd3dfd3d", "~/service/myoriginal.asmx?op=Add" },
{ "service/ldfdsfsdf/dsd3dfd3g", "~/service/myoriginal.asmx?op=Subtract" },
{ "service/ldfdsfsdf/dsd3dfd3k", "~/service/myoriginal.asmx?op=Multiply" },
{ "service/ldfdsfsdf/dsd3dfd3v", "~/service/myoriginal.asmx?op=Divide" },
});
}
}
NOTE: If you were to have MVC in the application, you should generally register your MVC routes after these routes.
References:
Creating a route for a .asmx Web Service with ASP.NET routing
.NET 4 URL Routing for Web Services (asmx)

Not a direct answer but something worth considering.
You could possibly upgrade your ASMX service to a WCF service with compatible contract so that you don't have to upgrade your clients at all.
With that, you could use a known technique to dynamically route WCF services. Since this known technique involves an arbitrary address for your service, you can bind the WCF service to a .......foo.asmx endpoint address so that your clients not only don't upgrade their client proxies but also they have exactly the same endpoint address.
In other words, to a client, your dynamically routed WCF service looks 1-1 identical as your old ASMX service.
We've succesfully used this technique over couple of last years to upgrade most of all old ASMXes to WCFs, preserving client proxies in many cases.
All technical details are documented in my blog entry
http://www.wiktorzychla.com/2014/08/dynamic-wcf-routing-and-easy-upgrading.html

The article you are referencing is not to provide extensionless routing to asmx WS, is to provide routing from server/whateverYouAre/ws.asmx to server/ws.asmx (the real resource location). This allows JS use local path (current location) to invoque the asmx without worry abot where the browser are.
Anyway, maybe, just maybe, you can use the article as starting point. I never do this so it just a guess:
There are 2 modes to consume your WS. If the client is using SOAP the request URL will be:
/server/service/myoriginal.asmx
with SOAPAction http header and the SOAP XML in the POST body. Your current routing solution should work. BUT if you are consuming the WS though raw HTTP GET/POST (i.e. from a webBrowser) the url of each webMethod is:
/server/service/myoriginal.asmx/webMethod
So I think you could to provide some url routing in the form of:
routes.Add("myservice", new System.Web.Routing.Route("service/sDxcdfG3SC/{webMethod}", new System.Web.Routing.RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/service/myoriginal.asmx")));
//Delete routes.MapPageRoute("", "service/sDxcdfG3SC", "~/service/myoriginal.asmx"); from your code, it is wrong even in your actual solution
and modify GetHttpHandler:
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath + "\" + requestContext.RouteData.Values["webMethod"], requestContext.HttpContext.Server.MapPath(_virtualPath));
}
to provide the raw URL of the requested resource in the form of /server/service/myoriginal.asmx/webMethod.
My code is write on the fly from the top of my head so just make sure _virtualPath + "/" + requestContext.RouteData.Values["webMethod"] create the right URL before a early rage quit ;-) Modify it if needed.
With some luck; WebServiceHandlerFactory should be able to locate the physical resource and, inspecting the raw URL, execute the webMethod by its name.

If the site is hosted in IIS you could use the IIS URL Rewrite to create a friendly url and redirect it to your internal path as per creating-rewrite-rules-for-the-url-rewrite-module. Each of these rules are stored in the web.config so can be managed within your development environment
The drawback (or benefit depending upon your usage) is that the original path would still be accessible

Related

Routing attribute in web api ignore part of uri

I have a controller
public class SimulatorController : ApiController
{
private SimulatorService _simulatorService;
public SimulatorController()
{
_simulatorService = new SimulatorService();
}
[HttpGet]
[Route("spiceusers")]
public async Task<IHttpActionResult> GetConsumerProductsAsync()
{
var consumerProductsList = await _simulatorService.GetConsumerProductsAsync();
return Ok(consumerProductsList);
}
}
And i have uri
http://comm-rpp-emulator.com/spiceusers/9656796/devicesfuse?includeComponents=true&groupByParentDevice=true&includeChildren=true&limit=50&page=1
I need that my method should processed
http://comm-rpp-emulator.com/spiceusers
and ignore othrer part of uri?
You can use the catch-all route parameters like {*segment} to capture the remaining portion of the URL path.
The assumption here is that attribute routing is already enabled.
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Web API routes
config.MapHttpAttributeRoutes();
// Other Web API configuration not shown.
}
}
The URL in the example posted can be matched to the action by using the catch-all route parameter which will capture the remaining portion of the URL path that matched the template
//GET spiceusers/{anything here}
[HttpGet]
[Route("~/spiceusers/{*url}")]
public async Task<IHttpActionResult> GetConsumerProductsAsync() { ... }
Now any calls to /spiceusers will map to the above action as intended.
Note that this also includes all sub calls below the spiceusers template, provided that is what was intended.
Note also that if this web api is being used along with the default MVC that the path my clash with the default routing. But given that wep api routes tend to be registered before MVC routes it may not be that much of an issue.

WebApi Path differences when using RouteAttribute or implicit routing

I've been working with WebApi and I'm trying to understand how the difference in using the Route Attribute vs having WebApi automatically create the route based on the function name works. I have many controllers of this form:
public class MyController : RestControllerBase
{
public async Task<HttpResponseMessage> GetData()
{
return _data;
}
[Route(ApiVersion.Version + "/MyController/{param}/specific/route")]
public async Task<HttpResponseMessage> GetMoreData()
{
return _moreData;
}
}
This controller and others all specify a route registry that does:
routingManager.RegisterVersionedHttpRoute(
routeName: "MyController",
routeTemplate: "MyController/{param}",
defaults: new { controller = "MyController", param = RouteParameter.Optional },
version: ApiVersion.Version);
When I build and test my API, both end points are correctly accessible.
I also overrode the HttpControllerSelector with one based on namespaces (for versioning).
My problem is that these two routes, besides the traditional function of being called by a specific http route, behave differently. For example, in my overridden controller selector I had to write this logic in order for the controllers and routes to properly map:
private string GetControllerName(IHttpRouteData routeData)
{
if (routeData.GetSubRoutes() != null)
{
// With route attribute
var subroute = routeData.GetSubRoutes().FirstOrDefault();
var dataTokenValue = subroute.Route.DataTokens["actions"];
if (dataTokenValue == null) return null;
var controllername = ((HttpActionDescriptor[])dataTokenValue).First().ControllerDescriptor.ControllerName.Replace("Controller", string.Empty);
return controllername;
}
else
{
// Try Strategy for without route attribute.
return (String)routeData.Values["controller"];
}
}
private string GetVersion(IHttpRouteData routeData)
{
var routeTemplate = "";
if (routeData.GetSubRoutes() != null)
{
var subRouteData = routeData.GetSubRoutes().FirstOrDefault();
if (subRouteData == null) return null;
routeTemplate = subRouteData.Route.RouteTemplate;
}
else
{
routeTemplate = routeData.Route.RouteTemplate;
}
var index = routeTemplate.IndexOf("/");
if (index == -1)
{
return Unversioned;
}
var version = routeTemplate.Substring(0, index).ToUpperInvariant();
return version;
}
It seems as though routes with the explicit attribute will have a route.GetSubroutes() available, whereas the implicit routes will not.
This causes me to write two different approaches to extracting the version and controller names from the incoming request before I can route them properly. I can live with that, but it really bothers me that something internal to WebApi is creating this distinction between the routes.
Unfortunately, I then started working with https://github.com/domaindrivendev/Swashbuckle to document my api and ran into similar issues. The swagger page generates correctly for routes that have the Route attribute, but completely ignores the implicit routes. I suspect the problem causing this issue is the same that causes the branch in my selecting logic.
I've been digging through as much ApiExplorer, WebAPI and other documentation as I can find online, but I haven't been able to find the solution to homogenize the implicit/explicit route approaches. It may even be a bug in their implementation.
Where should I go from here?
So the answer here is that there is a bug or inconsistency with Web Api 2
To verify, you can run this code snippet. In the case I mentioned, ApiExplorer does not return results for paths that are not explicitedly marked with a route attribute.
IApiExplorer apiExplorer = configuration.Services.GetApiExplorer();
foreach (ApiDescription api in apiExplorer.ApiDescriptions)
{
Console.WriteLine("Uri path: {0}", api.RelativePath);
Console.WriteLine("HTTP method: {0}", api.HttpMethod);
foreach (ApiParameterDescription parameter in api.ParameterDescriptions)
{
Console.WriteLine("Parameter: {0} - {1}", parameter.Name, parameter.Source);
}
Console.WriteLine();
}

Passing Complex Objects to ASP.NET Web API Using FromUri

I want to bind the URL parameters to my Point object using attribute routing and the [FromUri] attribute such that the following URL is possible:
/foo-1,2
public IHttpActionResult PostFoo(
[FromBody] string content,
[FromUri] Point point)
{
}
public class Point
{
public int A { get; set; }
public int B { get; set; }
// ...Other properties omitted for simplicity
}
I have tried the following Route attributes but none of these work:
[Route("foo-{a},{b}")]
[Route("foo-{A},{B}")]
[Route("foo-{point.A},{point.B}")]
Note that I can't use query string parameters because a badly built third party service does not accept ampersands in their URL's (Yes it's that bad). So I'm trying to build all query string parameters into the URL itself.
The two Options I'm aware of is:
Use URL Rewriter to Globally take care of every and all routes. The advantage is that (I would hope) your publisher does have some type of standard url you can transform into a friendly MVC route.
If not then you'll probably have to write your own RouteHandler. Not sure if you could use this globally, but you'd have to register it a lot (not that hard really).
public class CustomRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var acceptValue = requestContext.HttpContext.Request.Headers["Accept"];
if( /* do something with the accept value */)
{
// Set the new route value in the
// requestContext.RouteData.Values dictionary
// e.g. requestContext.RouteData.Values["action"] = "Customer";
}
return base.GetHttpHandler(requestContext);
}
}
Then register it:
RouteTable.Routes.MapRoute(
name: "Custom",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
).RouteHandler = new CustomRouteHandler();

MVC IgnoreRoute /?_escaped_fragment_= to continue Reverse Proxy with IIS ARR

Technical Information
AngularJS single page app
Umbraco 7.3.0 website, extended to register routes via Umbraco.Core.ApplicationEventHandler in a separate class library
Scenario
I have an AngularJS single page app (SPA) that I'm trying to pre-render via an external PhantomJS service.
I want MVC's route handler to ignore the route /?_escaped_fragment_={fragment}, so the request can be handled directly by ASP.NET and thus passed on to IIS to proxy the request.
In Theory
Umbraco is built on ASP.NET MVC.
Routes are configurable via System.Web.Routing.RouteCollection class.
When extending Umbraco with custom routes, any routes configured via the System.Web.Routing.RouteTable will take precedence over Umbraco routes, thus never being handled by Umbraco**
Possible methods for my scenario
public void Ignore(string url) or
public void Ignore(string url, object constraints)
**I could be wrong. As far as I'm aware, custom routing takes precedence as it's done before the Umbraco routes are registered. However I'm unsure whether telling MVC to ignore a route would also prevent Umbraco from handling that route.
In Practise
I have attempted to ignore the routes with the following:
Attempt one:
routes.Ignore("?_escaped_fragment_={*pathInfo}");
This throws an error: The route URL cannot start with a '/' or '~' character and it cannot contain a '?' character.
Attempt two:
routes.Ignore("{*escapedfragment}", new { escapedfragment = #".*\?_escaped_fragment_=\/(.*)" });
This didn't result in an error, however Umbraco still picked up the request and handed me back my root page. Regex validation on Regexr.
Questions
Can MVC actually ignore a route based on its query string?
Is my knowledge of Umbraco's routing correct?
Is my regex correct?
Or am I missing something?
The built-in routing behavior doesn't take the query string into consideration. However, routing is extensible and can be based on query string if needed.
The simplest solution is to make a custom RouteBase subclass that can detect your query string, and then use the StopRoutingHandler to ensure the route doesn't function.
public class IgnoreQueryStringKeyRoute : RouteBase
{
private readonly string queryStringKey;
public IgnoreQueryStringKeyRoute(string queryStringKey)
{
if (string.IsNullOrWhiteSpace(queryStringKey))
throw new ArgumentNullException("queryStringKey is required");
this.queryStringKey = queryStringKey;
}
public override RouteData GetRouteData(HttpContextBase httpContext)
{
if (httpContext.Request.QueryString.AllKeys.Any(x => x == queryStringKey))
{
return new RouteData(this, new StopRoutingHandler());
}
// Tell MVC this route did not match
return null;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
// Tell MVC this route did not match
return null;
}
}
Usage
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// This route should go first
routes.Add(
name: "IgnoreQuery",
item: new IgnoreQueryStringKeyRoute("_escaped_fragment_"));
// Any other routes should be registered after...
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

Mapping to an ASMX service using routing in ASP.NET MVC

I wonder if there's any way to map a URL to ASMX service much like it is done with pages (using routes.MapPageRoute() method).
When I tried to do it simply by pointing the MapPageRoute to my service I get the error
Type 'MvcApplication1.Services.EchoService' does not inherit from 'System.Web.UI.Page'.
Matthias.
I stumbled upon this question trying to find the answer myself, and since I did figure out a way to do it, I figured I'd answer it.
The reason I needed this is because I'm converting an old ASP.NET website to ASP.NET MVC, and for compatibility purposes I need a web service available at a specific URL. However, the path of that URL is now handled by a Controller in the new site, so I cannot have a physical directory with the same name (since that will prevent the controller from being invoked for other URLs with that path other than the web service).
The PageRouteHandler, which is used by RouteCollection.MapPageRoute, indeed requires that the handler for the target path derives from System.Web.Page, which isn't the case for web services. So instead, it is necessary to create a custom page handler:
using System;
using System.Web;
using System.Web.Routing;
using System.Web.Services.Protocols;
public class ServiceRouteHandler : IRouteHandler
{
private readonly string _virtualPath;
private readonly WebServiceHandlerFactory _handlerFactory = new WebServiceHandlerFactory();
public ServiceRouteHandler(string virtualPath)
{
if( virtualPath == null )
throw new ArgumentNullException("virtualPath");
if( !virtualPath.StartsWith("~/") )
throw new ArgumentException("Virtual path must start with ~/", "virtualPath");
_virtualPath = virtualPath;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
// Note: can't pass requestContext.HttpContext as the first parameter because that's
// type HttpContextBase, while GetHandler wants HttpContext.
return _handlerFactory.GetHandler(HttpContext.Current, requestContext.HttpContext.Request.HttpMethod, _virtualPath, requestContext.HttpContext.Server.MapPath(_virtualPath));
}
}
This route handler will create an appropriate handler for the web service based on the request and mapped virtual path of the service.
You can add a route for a web service now as follows:
routes.Add("RouteName", new Route("path/to/your/service", new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler("~/actualservice.asmx")));
Note: you must specify the controller and action values in the route value dictionary (even though they're set to null), otherwise the Html.ActionLink helper will always use this route for every single link (unless a match was found in the list before this route). Since you probably want to add this route before the default MVC route, it's important that it doesn't get matched that way.
Of course, you can create your own extension method to alleviate this task:
public static Route MapServiceRoute(this RouteCollection routes, string routeName, string url, string virtualPath)
{
if( routes == null )
throw new ArgumentNullException("routes");
Route route = new Route(url, new RouteValueDictionary() { { "controller", null }, { "action", null } }, new ServiceRouteHandler(virtualPath));
routes.Add(routeName, route);
return route;
}
After which you can simply do:
routes.MapServiceRoute("RouteName", "path/to/your/service", "~/actualservice.asmx");
I hope this helps someone, despite the age of this question. :)
Now that we waited two years with an anwer, how about using Web API instead? :)
EDIT: Kidding aside if that doesn't work for you and you still need an answer, leave a comment and I will see if I can't come up with a better one.
I attempted the original post's solution (also posted here), but I encountered a serious problem. I couldn't target the web method within the web service. When attempting to do so I got an exception stating the file didn't exist.
If you truly want to map an MVC route to a .ASMX web service the solution is explained here.
I believe that solution to be a hack by abusing the built-in types, because it uses reflection to bypass the intentional restrictive access members on the built-in .NET types.
Here is the method I'm taking which I believe to be much more straightforward.
First off, you should design your web services in the .ASMX file so that all the web service does is act as a published interface. At that point we don't need to target the .ASMX web service's methods directly. The important code has been made re-useable in core classes that are agnostic to the application's entry-point. We need this anyway so we can run automated tests!
Replace the MVC's web service method with a new route that has a custom route handler and http handler.
Old Route:
routes.MapRoute(
"Lead",
"lead/{action}.mvc",
new { controller = "Lead" });
New Route:
var dict = new RouteValueDictionary
{
{ "controller", null },
{ "action", null }
};
var handler = new LeadRouteHandler();
var route = new Route("lead/MVC_General.mvc", dict, handler);
routes.Add("Lead", route);
Note that the new route has the action hard-coded "MVC_General". This is because I wish to improve upon the giant controller class and create a handler for each action that way I can have small class with a single responsibility for each web method.
Implement the route's handlers.
IRouteHandler:
public class LeadRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new LeadHttpHandler();
}
}
IHttpHandler:
public class LeadHttpHandler : IHttpHandler
{
public bool IsReusable
{
get { return false; }
}
public void ProcessRequest(HttpContext context)
{
// Just enough code to preserve the route's json interface for tests
var typedResult = new PsaLeadSubmissionResult();
typedResult.Registered = false;
typedResult.Message = new List<string>
{
"Not Implemented"
};
var jsonResult = JsonConvert.SerializeObject(typedResult);
context.Response.Write(jsonResult);
}
}
From within IHttpHandler's ProcessRequest method we gain full control over that route's action. With a well designed web service architecture all we need to do is call the class's that support the .ASMX web method you are trying to map the route to.
The result is a very clean Global.asax file. We could have done all of this without the URL routing just by manually inspecting the URL, but that's too important of a file to bloat.

Categories

Resources