How to call a controller outside of an area? - c#

In my application I have an area "Member".
Outside this member area I have a folder named "Generic" which is having a controller "DataBindController".
This controller will be used in all areas. So to keep it common, i am keeping it in a separate folder outside of areas.
My route config is as follows:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var ObjRoute = routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults:new {controller = "Login", action = "MemberLogin", id = UrlParameter.Optional},
namespaces: new string[] { "MyApp.Generic.*" }).
DataTokens = new RouteValueDictionary(new { area = "Member"});
//ObjRoute.DataTokens["UseNamespaceFallback"] = false;
}
}
Here's the project directory structure.
The Test controller inside Generic folder is as follows:
namespace MyApp.Generic
{
public class DataBindController : Controller
{
public ActionResult Test()
{
return Content("Test");
}
}
}
I am getting following error when I call the test controller using "http://localhost/MyApp/Generic/DataBind/Test"
Error in Path :/MyApp/Generic/DataBind/Test The controller for path
'/MyApp/Generic/DataBind/Test' was not found or does not implement
IController.
Please give me some idea on this issue.

your address doesn't match your defined route. Generic is just a folder. MVC doesn't care about the folder your controller is in.
the correct one should be like this :
http://localhost/MyApp/DataBind/Test
so the DataBind will be the controller and Test the Action.
Update:
your route is
{controller}/{action}/{id}
When you have Generic in your address, Asp.net matchs parts this way : Generic is the Controller, DataBind is Action and Test is the Id. of course it can't find such a thing. But when you remove Generic , every part goes to its real place.
To have Generic in the address, you should change your route to this:
Generic/{controller}/{action}/{id}
To read more about routing :
http://www.asp.net/mvc/overview/older-versions-1/controllers-and-routing/asp-net-mvc-routing-overview-cs

Related

URL Routing MVC 5 Asp.net

I know about routing in MVC. I added a new MapRoute under RegisterRoute method in RouteConfig.cs class and successfully called my function with the URL http://localhost:53363/package/PackageDetail/mypackage/5.
However, my question is do i have to add different Map Routes for every method or is there any better way ? Like in PackageController class you can see i have two methods one methods takes PackageId and PackageName and the other takes only PackageId. So do i have to register different Map Routes or not ?
RouteConfig
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Package",
url: "Package/PackageDetail/{packageName}/{packageId}",
defaults: new { controller = "Package", action = "PackageDetail", packageName = UrlParameter.Optional, packageId = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
PackageController.cs :
[HttpGet]
public ActionResult PackageListing(int packageId = 0)
{
return View();
}
[HttpGet]
public ActionResult PackageDetail(string packageName = "", int packageId = 0)
{
return View();
}
Despite the fact that Muhammed's answer will work, it is very repetitive, especially if you're using the same style of routes for multiple types.
There are a few things to consider before deciding upon a single approach to routing. The main one is why have both the name and ID in the route? If you want a more SEO friendly URL structure, don't bother with the ID at all.
If you have multiple products within the same type that have identical names, then there's no point in including the name as part of the URL since that won't get a user where they want to go by itself. In that event, just leave the original route.
However, if you have several different controllers (or actions) with a similar name/id structure for the routes, you'll be far better served with making your custom route more generic.
routes.MapRoute(
name: "NameAndId",
url: "{controller}/{action}/{name}/{id:int}",
defaults: new
{
controller = "Package",
action = "PackageDetail",
name = UrlParameter.Optional,
id = UrlParameter.Optional
});
Keep this above the default route, and this will redirect not just
/Package/PackageDetail/Deluxe/5
but also allow you to have stuff like this:
/Meals/Menu/Dinner/3
That may not necessarily be applicable for you in this project, but since you're learning MVC, this is a good skill to pick up. The more generic you're able to maintain your route definitions, the less you'll need to repeat it. Of course, if this is a one-time special route, there's nothing wrong with using the attributes.
Also to answer your final question, you do not need to create another custom route, because your PackageListing method will be routed through the default route that was provided when you created your project.
If you want to override default route url and generate custom url then you need to register route in route config file.
You can pass Package name and package Id as below.
http://sitename/Package/PackageListing?packageId=1
http://sitename/Package/PackageDetail?packageName=packagename&packageId=1
but if you want to generate URL as below than you need to add route in route.config file.
http://sitename/Package/PackageListing/1
http://sitename/Package/PackageDetail/packageName/1

ASP.NET MVC Attribute & Conventional Routing not working

I am trying to configure routing with both Conventional & Attribute based.
If I just use the default Conventional route included with MVC everything works. but if I add this Route attribute, I get a 404.
Here is the GET request URL: http://localhost:52386/Home/SimpleSearch?searchTerms=test&dateRange=0
Here is my RouteAttributes in Code:
[RoutePrefix("Home")]
public class HomeController : Controller
{
[Route("SimpleSearch/{searchTerms}/{dateRange}/{page?}")]
[HttpGet]
public ActionResult SimpleSearch(string searchTerms, DateRangeEnum dateRange, int page = 1)
{
//Code here
}
}
Also the Route Config looks like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
//Default
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
I don't see what is wrong with this RouteAttribute, but even if something is wrong with it, why doesnt it fall back onto the default Conventional Route and work?
With the attribute route definition, you explicitly specified the route pattern to be
Home/SimpleSearch/{searchTerms}/{dateRange}/{page?}
So you should try to access your action method with same url pattern.
This should work.
http://localhost:52386/Home/SimpleSearch/test/0
and Model binder will be able to map "test" to searchTerms parameter and 0 to dateRange parameter.
Your conventional (explicitly using querystring) will not work when you have an attribute route with a different pattern

c# MVC 5 RouteConfig redirection

Recently I had to update my mvc webapplication so that a basic entity of the system is displayed in the UI with a different literal.
Lets say
Previously I had: "Vessels"
Now I am asked to make it: "Ships"
The urls where mapped as by convention: mysite/{controller}/{action}/{id}
So I had urls like :
mysite/Vessels/Record/1023
mysite/Vessels/CreateVessel
I did all the renaming in the User Interface so that the titles and labels are changed from Vessel to Ship and now I a m asked to take care of the urls as well.
Now, I do not want to rename the Controller names or the ActionResult method names, because it is some heavy refactoring and because it is VERY likely, that the literals will soon be required to change again... ;-)
Is there any quick solution for me by editing just the RouteConfig, or something like that, that could do the work with a couple of lines coding?
Yeah, just register the route that will map your VesselsController actions:
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Vessels", // Route name
"Ship/{action}Ship/{id}", // URL with parameters
new { controller = "Vessel", id = "" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
}
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
}
}
Also make sure to register your route before default one. Because, in other case, default route will be executed first and you will get an exception because no ShipController is defined in your application.

Issue regarding Setting an alternate controller folder location in ASP.NET MVC

from here i got a little touch about Setting an alternate controller folder location in ASP.NET MVC. here is the url Setting an alternate controller folder location in ASP.NET MVC
they guide us we can change the namespace and also specify the name space in routine code and these way we can solve it but this above link is not showing how to change or store controller related .cs files in other folder location.
suppose i want to store controller in folder called mycontroller in root then what i need to do. guide me please. thanks
UPDATE
You can do this using Routing, and keeping the controllers in separate namespaces. MapRoute lets you specify which namespace corresponds to a route.
Example
Given this controllers
namespace CustomControllerFactory.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return new ContentResult("Controllers");
}
}
}
namespace CustomControllerFactory.ServiceControllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
return new ContentResult("ServiceControllers");
}
}
}
And the following routing
routes.MapRoute(
"Services",
"Services/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "CustomControllerFactory.ServiceControllers" } // Namespace
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "CustomControllerFactory.Controllers"} // Namespace
);
You should expect the following responses
/Services/Home
ServiceController
/Home
Controllers
There is no need to do anything, storing controllers in Controllers is only convention, not a requirement. Basically you can store controllers in any folder.
Check your RouteConfig.cs and you will probably find that a namespace has been defined for each route. You controller classes can be placed anywhere in the project as long as they still match the namespace defined in your routes.

Controller in separate assembly and routing

In the same solution, there is a ASP.NET MVC4 application Slick.App and class library Awesome.Mvc.Lib. Awesome.Mvc.Lib contains one controller class.
public class ShinnyController : Controller
{
[HttpGet]
public string Index()
{
return "Hello, from Awesome.Mvc.Lib";
}
}
If I just add the reference from Slick.App to Awesome.Mvc.Lib, run the application and point brower to /shinny, I will actually see the response "Hello, from Awesome.Mvc.Lib".
This is something I don't expect at all. All the time I thought ASP.NET MVC respects the namespaces there the controllers placed in. So, the controllers from another namespaces are not exposed, at least before I didn't ask to.
I tried to change the default route registration, to use namespaces parameter.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new [] { "Slick.App.Controllers" }
);
Still, the ShinnyController route still match for '/shinny'.
I have a concerns this is right default behaviour. My question is, how to explicitly say which controllers are exposed and prevent default route to match controllers in separate class library?
The namespaces list on the route only gives priority to certain namespaces over the others, which are not listed :
new [] {"Namespace1", "Namespace2"}
doesn't give higher priority to Namespace1 as one would expect but just gives priority to both namespaces over the others.
This means that the namespaces in the list are first searched for controllers and then, if no match is found the rest of the available controllers with that name are used.
You can suppress the use of non prioritized controllers by doing this:
var myRoute = routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional },
namespaces: new [] { "Slick.App.Controllers" }
);
myRoute.DataTokens["UseNamespaceFallback"] = false;
You can inherit from DefaultControllerFactory like this:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override Type GetControllerType(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var type = base.GetControllerType(requestContext, controllerName);
if (type != null && IsIngored(type))
{
return null;
}
return type;
}
public static bool IsIngored(Type type)
{
return type.Assembly.GetCustomAttributes(typeof(IgnoreAssemblyAttribute), false).Any()
|| type.GetCustomAttributes(typeof(IgnoreControllerAttribute), false).Any();
}
}
Then some changes to Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(new CustomControllerFactory());
}
And here you are! Any type marked with IgnoreControllerAttribute won't be visible. You can even hide the whole assembly.
If you need some configuration based behaviour, it is not a great matter to make all necessary changes ;)

Categories