How does asp.net mvc figure it out? - asp.net

How is it that I can create a method in the controller and just put some arguments and it figures it out after I click on a form submit? Under the hood, how does it find the right method and how does it figure out that I just want those arguments?

In a nutshell:
The routing engine handles the HttpRequest, and checks the requested URL. When it finds the first route match, it creates a new instance of MvcRouteHandler and passes it the broken-up tokens of the URL in a RouteValueDictionary.
The route's MvcRouteHandler takes the request, and tries to instantiate a controller class instance. By convention, it looks for a class called "XXXXXXController", where the X's are replaced by the {controller} parameter in the route.
Once it finds the controller, it invokes the appropriate method on it, given by the {action} parameter of the route. Any named arguments, such as {id}, that exist in the route, are passed as parameters to the method.
Basically, everything that ASP.Net MVC "knows" comes from the route information. It can't divine the parameters from thin air - they have to come from the route parsing. If the information isn't present in the requested URL, it can't be passed into the method.
It should also be noted that you can override the behaviour of the framework by making your routes use alternate handlers instead of MvcRouteHandler. The framework is quite extensible, so you can plug in custom functionality at many points.

There's quite a bit of code in play for controller, action and view resolution, as well as the ModelBinders. So much that it'd probably be best for you to look into specific portions of the framework and ask a more detailed question to get much of an answer.
Luckily, the ASP.NET MVC framework has been open-sourced, so if you're curious as to how it all works, you can get the code and look through it yourself. Its excellent code to read through and you're sure to learn something.
More to the point of your question, however, you should look at the System.Web.Mvc.MvcHandler and System.Web.Mvc.ControllerActionInvoker classes, which should lead you down the right path for answering your questions.

Related

Symfony: How to handle common request scope data

I'm migrating a legacy PHP project (pre-OO) to Symfony2. On every request I have to:
compute some dynamic data (depending on the current date and/or some request parameter)
use that data (multiple times!) in the rendered response.
A naive approach would be:
At the start of every controller method, call some global helper function to compute the data.
At the end of every controller method, pass the data as a parameter to the twig template.
Sounds tedious. Maybe it would be better to:
Create a subscriber for request events that computes the data when a request comes in and provides access to it via getter methods.
Define that subscriber/service as a global twig variable in config.yml.
In twig templates, call the getter methods on that service as needed.
Is that viable?
In particular, are the twig variable/service and the subscriber always identical? Or could the service be a newly created instance?
Is this some sort of misuse? Or is there an officially recommended way for such a use case?
EDIT The data is not only needed in every twig template but in some controllers, too.
Calling a specific method in every Controller-Action would really be a bad solution. Your solution to use a subscriber isn't perfect either.
Without knowing your use-case in detail it´s hard to find a suitable way.
Maybe one approach would be to write a Twig-Extension and injecting a Service into this Extension. The Service would get the Request-Stack via Dependency-Injection and compute the relevant Data. You could then access this data "on demand" through the Twig-Extension during the rendering.
Another approach could be using sub-requests during the rendering (Symfony: How to handle common request scope data)
Maybe these tips already helped you a bit. Otherwise let me know more details, where / how you need the data during the rendering.

Extending ASP.NET MVC 4 MvcHandler

I'm trying to add some functionality to the default MvcHandler. What's happening is: I wanted to have dashed url's instead of Pascal Case url's. In other words if my controller is SomeController I wanted the URL to be /some-controller instead of /SomeController.
My best workaround was: I've created one mapping file URLMappings.xml which maps each controller to each desired URL. Then I've extended the default Route class to generate outgoing url's based on this and the default RouteHandler to understand the url's based on this. Well, this works fine because even if some mapping wasn't created then the framework will use the default behavior.
My point is: with this the routing system was understanding both kinds of Url's and this leads to duplicate content SEO problem. I wanted then to implement the following:
Get the controller value
See if on some mapping the controller name matches the value
If it matches, then there's some preferable URL than the one that was typed, should return 404.
I've searched on the web and the only way I've found to do this was to create a new IHttpHandler. However I don't want one from scratch, since I need all MVC functionality. I just want to put this logic on the ProcessRequest, however my overidden version of the method is not being executed.
Can someone give me some idea on how to deal with this ? Sorry if the question is silly or if it's not well detailed. If there's need for more information, just tell me.
You don't need a custom MvcHandler but a custom Route. There's a already NuGet package for this functionality called LowercaseRoutesMVC. Feel free to download it, explore the source code and adapt if necessary (to put the dash wherever you want to put it).

Organizing Master-Detail Controllers ASP.Net Web API

I am trying to determine the best way to implement the retrieval of detail records based upon the master's ID.
Obviously you would set up API controllers for both master and detail.
Solutions I've considered:
Have API consumers use OData to get all details filtered by a master ID. While I don't have an issue with this solution, I kinda feel bad putting that onto the API consumer and feel it is something that should be handled internally by the API
Go against the convention of just having the Get/Put/Post/Delete methods and create an action of "GetMastersDetails" on the detail controller and make it accessible via routing. While this would certainly work, I feel this gets away from the whole point of Web API (to an extent).
Create a 3rd controller named "MastersDetailsController" which would have a Get based upon a master ID with different possible return types:
Which would return a list of detail IDs which would then be used to call a Get on the details controller to get those actual details
Which would return a list of actual detail objects. What I don't like about that is having a controller returning a different type than what it is based upon.
Option 2 will be fine. Option 1 opens up a lot more risk depending on your scenario, and what you want to allow the user to get at.
It's not really "against convention" to add custom methods to an ApiController. You can do that however you like. It would only be "against convention" if you did so and used the wrong HTTP methods (i.e. a GET when you're deleting something in your custom method).
I'd go with either #1 or #2.
For #1, OData support enables not only the scenario you describe but offers a lot of additional functionality which might be desired in the future.
For #2, I don't think it gets away from the point of Web API's. Maybe a bit from a true RESTful service, but it's easy to implement and easy to understand.

Force case-sensitive routing in ASP.NET MVC

This question has been asked in a similar but not identical fashion (and not resolved to my satisfaction) previously on Stack Overflow and elsewhere.
Coming from a linux-world, I want to use ASP.NET MVC but avoid identical but differently-cased routes from resolving to the same page. I do not want to force all routes to be 100% lowercase.
e.g. I want /Home/Something to be a valid route and /Home/somethingElse to also be a valid route, but not /Home/something or /home/somethingelse, given two functions called Something and somethingElse in the HomeController.
I can't find any way of doing this from within the RegisterRoutes function, but maybe I'm missing something obvious? I can answer this easily enough by adding code to each Controller function, but I'm obviously trying to avoid doing that.
Optimally, the solution would involve catching all permutations of a particular route, then 301 redirecting any that do not exactly match the case of the controller's function.
I was unable to find any way of doing this after extensive searching. Basically, case-sensitivity and IIS/ASP.NET apparently do not go together.
We're now using a bit of a kludge to solve this. The code has been opensourced (MIT license) on github: NeoSmart Web Toolkit, in particular, this file containing the SEO redirect code.
Using it is easy enough: each GET method in the controller classes needs to add just this one line at the start:
Seo.SeoRedirect(this);
The SEO rewrite class automatically uses C# 5.0's Caller Info attributes to do the heavy lifting, making the code above strictly copy-and-paste.
Ideally, I would love to find a way to turn that line of code into an attribute. For instance, prefixing the controller methods with [CaseSensitive] would automatically have the same effect as writing in that line, but alas, I do not (yet) know how to do this.
I also can't find any way of figuring this out with the Routing class/structures. That's some opaque code!

given a url, can I get which route mapped? similiar to how mvc does it internally?

I want a function that, given a URL, returns me the route that matches.
This is exactly what MVC does under the covers, how do they do it?
I have the source, but not sure where to look.
It might match multiple routes, so something like Phil Haack's Route Debugger might be more useful. The source code is included.
Have a look at the MvcHandler class. Just a note, the ProcessRequest uses the RequestContext which is part of System.Web.Routing.
You could create your own method by deriving from the correct interfaces. This article may be of assistance

Resources