ASP.NET Routing: predict which route will be executed - c#

I have a RouteCollection and a string with an URL.
Has anybody an idea how can I get the name of the route that would be executed?
Some method like RouteCollection.PredictRoute(url) that will return the route name or the physical file that would be executed.
I want to create some statistics from web server log files. It would be very nice to reuse the used RouteCollection for that statistic.
Many thanks!

Here is a snippet of code I used to test that URLs were being mapped to their correct controller and action. You should be able to inspect the route name as well.
RouteConfig.RegisterRoutes(routes);
RouteData routeData = routes.GetRouteData(context);
string controller = routeData.Values["controller"];
string action = routeData.Values["action"];
The variable routes is a RouteCollection object and context is a HttpContextBase object.

I assume you want to do this outside of your MVC application, where requests and contexts are not available (only the log files, as you mentioned).
Unfortunately, that is not very easy to do. In order to write such a matching method you need to reuse (or worse, rewrite) the MVC framework code for URL parsing found in various classes in namespace System.Web.Http.Routing. The source code is available on CodePlex, under Apache 2.0 license.
Instead, I think there's an easier way you can include the route names in the log files and use them later in statistics. Have a look below.
What you could to do is extend the default MvcHandler class and override its ProcessRequest method. In there, you can call the base class implementation and right after it do your stuff by using RequestContext.RouteData.
The class RouteData contains, besides other things, also the current route (which you defined in your route collection. That is accessible through the RouteData.Route property.
This works because MvcHandler is always delegated (unless you've created your own MVC infrastructure) by the MvcRouteHandler, to handle the processing for each request. It is the one that gets the controller data from the existing route data, instantiates an IController implementation, and ultimately executes it.

Related

Restricted all routes expect the alowed one

Yeah, this might sound odd but we have a third party library which enable a sort of service consumed by a JavaScript application. This third party library take care of calling the correct controller and action.
This works all very well but we now need to restrict the routes to this "ServiceController" and maybe one or two other controllers. In the current version (MVC2) we simply override the controller factory which statically checks for a valid controller. Yeah it worked but it was more a workaround which then stayed there forever. So is there any clean way for archiving this (by configuring the routes? Note that we don't use the authorize attribute. And we will be using MVC4 in the next release)
Add global filter and put logic into OnActionExecuting (http://msdn.microsoft.com/en-us/library/gg416513(v=vs.98).aspx)
Option 1: Check routeValues for a correct controller and action
var controllerName = filterContext.RouteData.Values["controller"];
var actionName = filterContext.RouteData.Values["action"];
Option 2: Check this value Request.Url
Actually you allow only the routes defined and nothing else, so the question is really a little odd.

User Access Audit

I am working with a c# MVC website that has been developed over the course of several months by different people. The site is quite big and uses the .NET membership facilities regulate user access to various features.
I have now been tasked with a security audit, to list what users or roles have access to which features. I can do this by hand, but given the large number of controllers and their actions, it would be easier if I could do this with code. I am at a loss on how to even begin.
So, in short, how do I get a list of all the controllers on a site, their actions, and determine which, if any, users/roles have access to them?
You can use a little reflection to get controller methods. You can get your controllers:
var controllers = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.IsSubclassOf(Controller));
where Controller is the standard base class; if you've got your own custom base controller you can use that instead. This assumes you're running this code within the app; if that's not the case then replace Assembly.GetExecutingAssembly() with the appropriate logic to find your app's assembly. Assembly.GetExecutingAssembly() is easier, so I'd say to just add a function to do this somewhere in your admin-only area, if there is such a thing.
That line above will give you an array of System.Type objects for your controllers. From there, foreach (var TController in controllers) through them and get their methods:
var actions = new ReflectedControllerDescriptor(typeof(TController)).GetCanonicalActions();
This gets you an array of ActionDescriptor objects, from which you can use GetFilterAttributes() and GetCustomAttributes() to see what attributes are on that method - you're mostly looking for AuthorizeAttribute, AllowAnonymousAttribute, and your custom auth stuff. The ReflectedControllerDescriptor has these methods as well, for attributes applied to the entire controller instead of per-method.
Once you know which auth-related attributes are set, you can inspect the properties of those methods to see what they're set to. For example, AuthorizeAttribute exposes a Roles property, so for a method marked [Authorise("admin", "superuser")] you'll get a string containing those terms, so with a little logic you can split it up into the individual role names and use those for grouping/sorting/whatever. For any custom attributes... well, that depends on how you've implemented them, but if they don't expose what you need you can always edit them so they do.
If your methods use something like User.IsInRole internally... well, I'm not sure there's a way to find that programmatically. You might be stuck using Call Hierarchy view or Find All References to see where that sort of method is being accessed, so you can sift through manually.
Meanwhile, as part of this audit it's probably worth logging what's actually being accessed, if you don't already. In my projects, I tend to implement an action filter inheriting from ActionFilterAttribute, using some logic that inspects the attributes as described above, and using the ActionDescriptor and HttpContext.User that get passed in as part of the filter context to record what action is being accessed and who's accessing it.

Curious about MVC Controllers?

Just a quick question, when you create a new controller for a new MVC ASP.Net app how does it know which controller to use. More specifically, given you create a new controller and you call it SockController in order to use said controller I would navigate to http://mywebapp/sock. How did the web app know that /Sock/ is linked to SockController? Is there a mapping somewhere ? Or if not what happens when if you call omit controller from the name when creating it, ie call it SockCont.
Note: I am not a Web Dev im just curious so please don't post links to page with tons of text, im looking for a short simple answer.
ASP.NET Routing extracts the name of the controller from the URL by getting the Route Value and then appending "Controller" to the end. So "/home/" returns "HomeController".
ASP.NET then uses reflection to go through every class in the project's assembly (or referenced assemblies) to find a class that inherits from System.Web.Mvc.Controller and is called "HomeController". It then uses the default, parameterless constructor to create an instance of it.
It then matches the Route action to a method of the controller.
This process is called "Dispatch" and similar patterns are seen in PHP, Ruby-on-Rails, etc, except that dynamic languages like those have different ways of resolving class names to actual objects (CakePHP uses Class auto-loading bindings to locate the class definition, for example).

Change the Location of the Controller inside the Project structure

I found this Post (How to extend where MVC looks for views) about changing the location of the View.
I was wondering if there's something similar for changing the location of the controller.
I just want to change the location of the class inside project and don't want to affect the url.
For example Instead of placing the Controller into
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
I want to achieve something like
MyMvcProject\MyGroup1\
MyController1.cs
MyController2.cs
MyMvcProject\MyGroup2\
MyController3.cs
and also support Areas:
MyMvcProject\Areas\MyGroup3\
MyController4.cs
Is it possible to achieve this? And if yes, where can I find documentation about it?
You can do what you want, and it doesn't require any special configuration, because ASP.NET MVC does not care about where you put your controllers. First, controllers are located using reflection, so the name of the folder where you put your controllers is irrelevant. Controllers are searched by type name and optionally by namespace (for disambiguation). You can even have controllers in separate projects/assemblies. As long as the controller ends up in an assembly in the bin folder then it's searchable by the framework.
As mentioned above, you'll need to create a controller factory to support your custom resolution. Here's an example:
http://develoq.net/2010/custom-controller-factory-in-asp-net-mvc/
As others have already stated you need to do one of the following:
Derive from IControllerFactory interface and provide an implementation of the CreateController and ReleaseController methods.
Derive from DefaultControllerFactory and override the default behaviours.
Here are some links to get you started:
Custom controller factory in ASP.Net
Inside the ASP.NET MVC Controller factory
Dive deep into MVC - IControllerFactory
Also, if you're willing to spend a bit of money I would also recommend the book Pro ASP.NET MVC 3 Framework as this explains almost every aspect of how the MVC framework can be customised (including an example on how to create a custom controller factory - the source code for which can be freely downloaded from the publishers website).
I think it is impossible to do this. ASP.NET MVC have defined the convention that we have to follow.
Controllers are in Controllers folder, views are in Views{ControllerName}\
I believe you cannot change the convention unless you create your own ControllerFactory.
If you really want to do that, just implement IControllerFactory interface (or try to derive from DefaultControllerFactory).
Then your Application_Start register your controller factory using ControllerBuilder.Current.SetControllerFactory method.
Look at the ControllerFactory documentation and to the MVC source code for details.
What you're asking and what your example shows are two different things; depending on which one you want to achieve, you may or may not need to do any work.
There are two requirements for a class to be a controller in the MVC Framework:
It has to have a class name of Name + "Controller"
It has to have a parameterless public constructor.
Your sample "normal" MVC layout is actually not valid:
MyMvcProject\Controllers\
MyController1.cs
MyController2.cs
MyController3.cs
Those classes wouldn't be found by MVC because they don't have the correct name, regardless of which folder they are in.
If all you want to do is change the namespace/folder names, that "just works", assuming you name them the same as the appropriate route segment(s):
MyMvcProject\MyGroup1\
Page1Controller.cs
Page2Controller.cs
MyMvcProject\MyGroup2\
Page3Controller.cs
MyMvcProject\Areas\Area1\
Area1Page1Controller.cs
This walkthrough (written for MVC 2 but works just as well in MVC3) shows you how to support Areas with the default controller behavior.
If you actually want to name them SomethingController1 or SomethingElseController5, or otherwise change the route -> classname mappings, then you do need to implement a custom ControllerFactory, and inject it into the MVC pipeline.
There are plenty of examples on the web on how to do this, including the one posted earlier.

How to ignore all of a particular file extension in MVC routing

How do I configure an IgnoreRoute to ignore all files with a certain extension, regardless of what directory they're in?
If I do this, everything works and my Flash movie gets played:
routes.Ignore("community/123/plan/456/video/moviename.flv");
So the "123" and "456" sections are variable and can be any integer number. Obviously though, I don't want to do one of these for each movie NOR do I have a need to replace 123 and 456 with variable placeholders. This is only an example of one type of directory, there are Flash movies stored throughout the application so what I need is an IgnoreRoute value that will ignore files that have a .flv extension no matter where in the hierarchy they are.
I've tried the following:
routes.IgnoreRoute("{file}.flv");
routes.IgnoreRoute("(.*).flv(.*)"); // Yeah I know, I'm horrible at RegEx
The only thing I can get to work so far is specifically passing the full relative path to the FLV file. Any suggestions?
Check this article by Phil Haack: http://haacked.com/archive/2008/07/14/make-routing-ignore-requests-for-a-file-extension.aspx
Long story short, we didn’t want routing to attempt to route requests
for static files such as images. Unfortunately, this caused us a
headache when we remembered that many features of ASP.NET make
requests for .axd files which do not exist on disk.
To fix this, we included a new extension method on RouteCollection,
IgnoreRoute, that creates a Route mapped to the StopRoutingHandler
route handler (class that implements IRouteHandler). Effectively, any
request that matches an “ignore route” will be ignored by routing and
normal ASP.NET handling will occur based on existing http handler
mappings.
Hence in our default template, you’ll notice we have the following
route defined.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
This handles the standard .axd requests.
........
We only allow one catch-all route and it must happen at the end of the
URL. However, you can take the following approach. In this example, I
added the following two routes.
routes.IgnoreRoute("{*allaspx}", new {allaspx=#".*\.aspx(/.*)?"});
routes.IgnoreRoute("{*favicon}", new {favicon=#"(.*/)?favicon.ico(/.*)?"});
What I’m doing here is a
technique Eilon showed me which is to map all URLs to these routes,
but then restrict which routes to ignore via the constraints
dictionary. So in this case, these routes will match (and thus ignore)
all requests for favicon.ico (no matter which directory) as well as
requests for a .aspx file. Since we told routing to ignore these
requests, normal ASP.NET processing of these requests will occur.

Categories

Resources