Redirect Controller to Another Controller - c#

Say for example, I have ControllerA which has about 10 Actions in it, when a user types into the URL /ControllerA/{Any Action name} it should redirect to /ControllerB/Index
Is there a simple way to redirect all actions in a ControllerA to ControllerB without having to write RedirectToAction in all the actions in ControllerA

The question is not very clear -
I can see potentially three different scenarios (maybe more?)
1. Do you actually need to redirect from all these actions before doing anything (so why are they even there?),
2. Do you want them to execute and then go to ControllerB.
3. Do you just want to invoke ControllerB directly for all these 10 Urls?
For scenario one you can just use attribute routing on controllerA (or even on controllerB) and have a single method that redirects to index.
For scenario two you want to override OnActionExecuted
For scenario three you want to put attribute routes directly on Index with all the paths that lead there.
Edit:
you can do something in routing that will short circuit controller A.
if (configurationCondition)
{
routes.MapRoute("Hijaack",
url: "controllerA/{*theRest}",
defaults: new { controller = "controllerB", action = "Index" });
}
This is more loose than requiring the actions to match exactly to what is on controllerA. It avoids the redirect completely, which should be a nicer experience to the end user.
It can be tightened up to more specific routes with attribute routing.

There are a few other way of doing this without affecting the ControllerA which would be more suitable:
by using a filter in which when you see that the user is asking for this controller you return a redirect response to the 2nd controller
by using a routing rule
Both can be implemented by taking care of your web config settings.

Related

MVC request handler

ASP.NET maps HTTP requests to HTTP handlers based on a file name extension. I was just wondering how does it work in MVC, because in MVC we just point to the action method inside the controller. There is no extention we specify.
Then I tried to understand how handler works in MVC Application execution process from this link
But still I did not understand. Can anybody explain how handlers work in MVC and how it is different from normal ASP.NET Handlers.
In fact, a comprehensive explanation here would be really out of scope, you should read about it to learn it in depth, and if you accept an advise, if you are able to find one, a book is generally a better resource than blogposts and the like (in my experience, as a blog post could be written by anyone who might not have a clue). Just make sure you read the reviews to find out whether a book is good or not so you won't get mislead.
Anyway, I'll try to outline the basics, without adding too much noise with the details because that would lead too far.
As others have already mentioned in their answers, in the MVC ecosystem, there is a concept called routes and routing table. Basically, what they are, is a collection of patterns. When a request arrives, the request header contains the requested url, such as these:
http://localhost:12345/
http://localhost:12345/home/index
http://localhost:12345/products/catalog
http://localhost:12345/products/details/1
http://localhost:12345/promotion
There is a reason I've written more than one, and with different formats. Let's examine them one by one.
The http://localhost:12345/ example
The first, http://localhost:12345/ is the simplest one possible, it is usually referred to as default route. Usually this points to a parameterless Index action method in the HomeController, in the default area (that is, in "no area"). Note that both the action and the controller, as well as the area (and, well, pretty much any detail) is configurable. You make this work by adding the default route, but this is usually done for you by the project template. You can find it in the App_Start/RouteConfig file, it has a name parameter of "Default":
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
So, whenever a request comes in which no (let me say this in a primitive way) additional parts are present, they get handled by the Index action inside the HomeController. As you can see in the code above, Index and HomeController are configured in the defaults: parameter, it is there where you have to go to in order to change the defaults. If you instead use an action called Welcome, then you would need to pass this to the method above:
defaults: new { controller = "Home", action = "Welcome", id = UrlParameter.Optional }
Also note that despite I've written "in which no additional parts are present", by additional parts I only mean route segments -- for example, a query string ?param1=value1 will not prevent this route from being selected.
The http://localhost:12345/home/index example
This is the exact same as above. I've only included this so that you can see that even though this particular route is the default, nothing prevents you from actually specifying the controller and the action.
The http://localhost:12345/products/catalog example
The basic workings of this one is the same as the above two, except that this is not a default route, so you need to explicitly specify the controller and the action in order for your request to be routed to the appropriate controller and action. Look again at the configuration code above. You can see that there is a url pattern of "{controller}/{action}/{id}" defined. You do not need to create a new route because this pattern can be used for this example. Compare them and you can see why:
http://localhost:12345/products /catalog
{controller}/{action}/{id}
As you can see from the displacements, products is recognized as the controller name, catalog is recognized as the action name, and since id is given a default value of UrlParameter.Optional, the value of it may be omitted and the route will still be valid for the request. So as long as there exists a ProductsController in the default area and within it, a Catalog action which either has an id parameter, or has no parameters at all, or any existent parameter has a default value (or is nullable, in which case it will be passed null), the routing system will use this route for the request.
The http://localhost:12345/products/details/1 example
Same as above, but here, 1 is mapped to the id segment. As long as there is a ProductsController in the default area, and within it, there exists a Details action, which either has no parameters at all (since id is optinal, it may be ignored), or has a parameter called id and if every other parameters that might be present have a default value (or can be defaulted to null - which means it is a reference type), then this route can be used for the request.
The http://localhost:12345/promotion example.
This is a special one I've included for one particular purpose. Let's assume you do not have a PromotionController. You can still define a route pattern as so:
routes.MapRoute(
name: "Promo",
url: "promotion",
defaults: new { controller = "Products", action = "Promotion" }
);
As you can see, here we assume you have a ProductsController and within it, a Promotion action. Whereas you could simply request the url http://localhost:12345/products/promotion (and it will actually work) specifying a "dedicated" route can also be done. I've added this example for the following reasons:
So that you can see such a thing is possible
Because there might be cases when you need it, such as:
The path at which a particular functionality could earlier be found, is now changed, but because your users might have bookmarked that page with the old url, you don't want them to see 404s. In this case, this route is used to "reroute" (not an HTTP Redirect, just couldn't find a better word) the old url to the new one.
In some cases, you do not want to expose the controller/action hierarchy. In the case of this example, simply typing "promotion" might be a little bit more telling to the user than typing "products/promotion". You can argue with this in this example, but still, there might be better examples in which case this is feasible. Also, you can use this to provide shorter urls.
And finally, let's remark a few things about the routing system as a whole.
There might be cases when an url could be mapped to more than one routes. In this case, the order in which the patterns are defined is the determining factor. The first one that matches the url gets picked which means that you should specify your route patterns starting with the most specific (aka with fewer or no varying parts at all). An example for a pattern with no varying parts is the last url I've written, it has a hardcoded pattern of promotion. You can recognize these from having no {}s in the pattern. The least specific (in other words, more general) ones should be at the bottom of the method. If there still are overlaps, you should rethink how you define your routes.
There are other things that can affect whether or not an action can be selected to serve a request. For example, the presence of [HttpPostAttribute] on an action method will tell the action invoker (and not the routing engine) that this method can only be used if the request is a POST request (unless an [HttpGetAttribute] is also applied). Note that the routing system and the so-called action invoker are two different things - you can find yourself in a situation where the routing system will find a pattern match for a route but the action invoker finds no suitable actions - for example, as above, a pattern is matched for which there is no GET handler but the request is actually an HTTP GET. This is beyond the scope of your question, but wanted to include this as a hint so that you are aware that you need to look it up. The HttpPost and HttpGet attributes mentioned above are special ones, in order to achieve such a functionality by implementing your own attributes, your attributes must derive from the ActionMethodSelectorAttribute class.
As you said in your question, requests are not mapped to files but to controllers and actions. So suppose a matching route pattern is found, and the controller to use happens to be Home(Controller) and the action to be Index. What the system does (this is not the responsibility of the routing system from here on) it scans the assembly of the running application (using reflection) and enumerates all the classes inside of it. It looks for classes that fulfill certain criteria (I'm not sure about whether or not the following is 100% accurate and/or comprehensive):
Derives directly or indirectly from Controller
Is not abstract
Is public (not sure about this one)
Has a public constructor (not sure about this either)
The name is either Home or HomeController (not sure if simply Home works, actually it is a convention to suffix the names with the term "Controller")
If none is found, then the request cannot be fulfilled as per the matching route. If more than one are found, then the one to use cannot be determined and an error is raised.
If exactly one matching class (Controller) is found, then the system will further investigate it. It will now look for methods that:
Are action methods, that is, their return type is ActionResult or a derived class (this is not required for AJAX-requests)
Are public
Its parameters satisfy the constraints defined by the route as discussed earlier
Any ActionMethodSelectorAttribute-derived attributes applied to a particular method do not prevent the action from being executed as per the request properties.
If no or more than one matching action methods are found, the request cannot be fulfilled and an error is raised.
If there is exactly one match, it gets invoked to fulfill the request.
Probably you have confused with the terminologies.
MVC uses routing tables to find the route to particular View.
Just like you have www.YourSite.com/index.aspx, You have www.YourSite.com/Home/Index.
The difference is that, in ASP.Net, you will be directly loading the .aspx file as in the URL and in MVC, you will go to the Home controller and look for an Index action. The Index action will then render it's view.
Here, each view or partial view will have their ActionMethod in a controller.
No view is rendered without a controller and action associated!
You could have a look at this link for further reference.
you will have a default route in the Global.asax file which will do the job.
If you have any complicated ones(with multiple parameters), You need to add them to the routing table manually. more about this is in the link.
Hope this hellps.
Don't confuse it too much
all controllers are class
all actions are methods
you are just calling the methods(functions) from address bar thats all...
the mvc handler check wither there is a method(ActionRsult) in class(Controller)
with the same name and matching params thats all

ASP.NET MVC 5 -Custom URLs with Attribute Routing and Dynamic controller and Action methods

NET MVC 5** I want urls like this
http://borntoprogram.com/.Net/.Net-Core/
I know i can achieve it using Attribute routing as shown below
[RoutePrefix(".Net")]
[Route("{action=Index}")]
public class NetController : Controller
{
[Route(".Net-Core")]
public ActionResult NetCore()
{
return View();
}
}
but i want to Generate Controllers and Actions methods dynamically. And use Attribute routing with that.
I want to use Dots (.) in URL , second thing i want the urls to be very simple having just 3 parts like
DomainName/CategoryOfArticle/ArticleName
as in the URL DomainName -BornToProgram.com , CategoryofArticle -.Net , ArticleName -.Net-Core
IN all I WANT to allow admin to Decide parts of the URL For new articles that he can submit on monthly or daily basis.
Like Category of Article (.Net in example)
and Then Name of the Article( .Net-Core , .Net-Framework)
The ADMIN want full control on the URLs
Since attributes are used to attach metadata to a class, it is not possible to dynamically add them at runtime. But even if you could, it is unlikely the Attribute Routing framework that reads and converts them into Route instances would function correctly.
If you need to make dynamically driven routes, the solution is to inherit RouteBase so you can add URLs and/or specify which controller they should refer to at runtime. You should cache the URL list as in the example, but there is no reason why you couldn't adapt the cache so individual URLs could be added to it one by one when they are added in your application (add them to the cache and the data source in one go, so they are both immediately and permanently available).

ASP.NET MVC 5: Define route order across controllers (RouteAttribute)

The RouteAttribute("abc", Order = 2) ordering only seems to be respected within a controller: when the following methods are defined in the same controller I get the expected behavior, i.e., Method1 takes priority, Method2 is never invoked.
[Route("abc", Order = 1)]
public ActionResult Method1() { ... }
[Route("abc", Order = 2)]
public ActionResult Method2() { ... }
If I define those methods in two separate controllers I get an ambiguous route exception: InvalidOperationException: Multiple controller types were found that match the URL. This can happen if attribute routes on multiple controllers match the requested URL.
Is it possible to define the order across controllers using attribute routing?
Edit 1:
It seems that even routes that would have an implicit ordering within one controller are ambiguous when spread over multiple controllers. The following would be okay within one controller (literal before wildcard), but throws an exception if placed into different controllers.
[Route("details/abc")]
[Route("details/{product}")]
This makes me that it is by design to keep controllers focused and force similar routes to be defined in the same controller.
Edit 2:
These are the routes I actually want to use. I want to put them into different controllers, because they do different things. They differ by the prefix.
[Route("reactive/monitor")]
[Route("{tier}/monitor")]
You have no constraints upon the route parameters, for your second route. If you were to define the route as [Route("{tier:int}/monitor")] you might not have the ambiguity. Alternatively, you can add a regex to the routes, to make them exclusive, something like {tier:regex(^(?!reactive))?} would let you resolve this.

Custom routes for controller with Html.ActionLink and Url.Action

Is it possible to create a exception in de Url.Action function for a certain controller?
We have a controller for Html pages in a database that can be accesses with a certain ID. For example /Page/1.
But instead of '/Page/1' we want to have '/seo-frienly-url' without the controller name or ID.
I know it is possible to add Custom routes in MVC, but with a lot of routes it gets slow when resolving them, so i want to make an exception for a certain controller.
The routing already works because we have changed the default route to '/{seo-page-title}' and linked it to a controller / action to get the html by page title instead of ID.
You can pass a route name to UrlHelper.RouteUrl. That way you can pick which route the URL must be built from.

Handling extra MVC parameter with different ActionResult

I'm working on a website that has 4 individual sections, each with their own controller. These controllers share a few models, however the model will do things slightly differently based on the controller calling it.
The route that I'd like to follow is {controller}/{model}/{id}/{action} so I could use controller1/cars/5/edit rather than controller1/editcar/5.
Is there any way to have my controller understand a model+action combination and handle them with different ActionResults, or am I forced to use an ActionResult on the model name and then use a conditional to figure out what action to take?
In Ruby I could use get "/controller1/cars/:id/edit" do. I'm just looking for something similar in MVC4.
Example routs:
/controller1/cars
(returns a view containing all cars from controller1)
/controller1/cars/5
(returns a view containing car with the ID 5 from controller1)
/controller1/cars/5/edit
(returns a view to edit car with ID 5 from controller1)
/controller2/cars
(returns a view containing all cars from controller2)
/controller2/boats
(returns a view containing all boats from controller2)
I think this route meets your needs. It requires some clever logic in your action methods but should give you the tools to handle it. Read my description of behavior.
routes.MapRoute(
"Default", // Route name
"{controller}/{model}/{id}/{action}", // URL with parameters
new { controller = "Home", action = "View", id = UrlParameter.Optional } // Parameter defaults
);
This route will Default to an action called View (that will presumably be used for Display) and has an option to direct to a different Action.
Model and id will be passed as arguments to you action method. Unfortunately, Model will not be sent as a type but a string (you may feed that into a Factory class).
if if is left out (eg /controller2/boats) it will be passed to your action as a null. This requires logic to handle but gives you the tools to handle it.
Thanks for the responses. The reason that I was having so much trouble with this was because I couldn't figure out how to separate controllers with a rout properly. This resulted in my idea breaking away from MVC standards and almost trying to implement a controller of controllers.
When I finally stumbled upon "Areas", I quickly realized that this is what I needed. I was able to add an Area for each section (or area) of my website, and within each area, I could define individual controllers and views, while my models remained in a shared directory outside of all areas. This now works perfectly.

Categories

Resources