How to remove default routes from WebApi? - asp.net-web-api

I can't found a way to remove default route paths for my Mobile service project based on WebApi. It generates routes such as "/api/{controller}/{id}" and "/tables/{controller}/{id}" which I don't want to provide for client apps. Instead of this I am mapping such route - "/api_v1/{controller}/{id}" and want to keep it as one possible route to access data.
Here is the code that doesn't work:
public static class WebApiConfig
{
public static void Register()
{
// Use this class to set configuration options for your mobile service
ConfigOptions options = new ConfigOptions();
options.LoginProviders.Add(typeof(CustomLoginProvider));
// Use this class to set WebAPI configuration options
HttpConfiguration config = ServiceConfig.Initialize(new ConfigBuilder(options));
// add login provider
config.SetIsHosted(true);
//foreach (var route in config.Routes)
// Console.WriteLine(route.ToString());
config.Routes.MapHttpRoute(
name: "api_v1",
routeTemplate: "api_v1/{controller}/{id}",
defaults: new { id = RouteParameter.Optional, } );
//Create the object of particular router
string result = "";
foreach (var r in config.Routes)
result += r.ToString();
// This does not remove any route.
// Also there is no 'Name' property for Route to know exact match
config.Routes.Remove("api");
}
}
Update:
I call config.Routes.Remove("") for every possible route i have and it removes only one name "tables". But "api" route is still there.
ServiceConfig.Config.Routes.Remove("DefaultApi"); // not removed any
ServiceConfig.Config.Routes.Remove("api"); // not removed any route
ServiceConfig.Config.Routes.Remove("tables"); // this works!
How to remove default 'api' route?

In App_Start, you should have a file called RouteConfig.
There's a method called RegisterRoutes in that class in which you will see pre-configured route mappings.
You can remove those as you see fit.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
additionnally, if you want to use routing by MVC attributes, you can add the following line:
routes.MapMvcAttributeRoutes();

Ok, config.Routes.Remove method should be used for this. To obtain route names you want to delete you may watch for them in debugger.
Default route name has name 'DefaultApis'.
So this code works:
ServiceConfig.Config.Routes.Remove("DefaultApis");
ServiceConfig.Config.Routes.Remove("tables");

Related

Web API route mapping to root

I am setting up an ASP.NET MVC 4 Web API to accept requests from a 3rd party server, and I simply can't figure out how to set the route mappings.
Assume the 3rd party server expects to get responses for:
http://[my_server]/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/release?user=[user name]&session=[session token]
Alternatively, the requests can use a dedicated path, i.e.:
http://[my_server]/***api***/authorize?user=[user name]&session=[session token]&item=[item]
http://[my_server]/***api***/release?user=[user name]&session=[session token]
I would like to be able to support both alternatives.
Additional requests, following the more traditional /controller/id form, should be implemented too, but I'd like to focus on the above (I'm not even sure that Web API is the way to go here).
I have written the following controller:
public class MyController : ApiController
{
[HttpGet]
[ActionName("authorize")]
public string Authorize(string user, string session, string item)
{
...
// return "OK" or "DENY";
}
[HttpGet]
[ActionName("release")]
public string Release(string user, string session)
{
...
return "OK";
}
}
and tried everything I could find in SO and elsewhere in WebAppConfig.Register, but I keep getting a 404 error when I try the request in the browser:
http://localhost:22332/api/authorize?user=ury&session=token&item=an_item
My question is, what do I have to do - specifically in WebAppConfig.Register and in the controller - in order to serve the above requests (assuming my test URL is correct...)?
You're getting tripped up by Web API conventions for matching a controller name in a URL to a controller class.
If the name of your controller is "MyController", then the URL to request is:
http://localhost:22332/api/my/authorize?user=ury&session=token&item=an_item
To support a request w/o the "api" bit in the URL, simply add a second route definition in the Register method of WebApiConfig.cs. Since you're also using "actions" in your URL's, you'd need these two routes:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "RootWebApiRoute",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
If you want to omit the "api" prefix as well as the controller parameter, I think you need a different route definition. Something like:
public static void Register(HttpConfiguration config) {
config.Routes.MapHttpRoute(
name: "ActionOnlyRoute",
routeTemplate: "{action}",
defaults: new { controller = "My" }
);
}
Here, we'll only look for an "action" in the URL and route everything to your "MyController" class.
After a few hours working on this and with a lot of help from the Route Debugger - Thanks Phil Haack! - I've found both the problem and the solution.
The problem: Route mapping matching is ordered, and RouteTable.Routes, from which the app's route mapping is initialized, contains quite a few of them. The request pattern I was looking for also matched some of these mappings ("authorize" was matched as a controller, for example).
The solution: add "my" route mappings before the default mappings.
Yeah, right...
Since most operations are not supported on HttpRouteCollection, the resulting code is a bit ugly, but it works:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
routesCopy = new RouteBase[RouteTable.Routes.Count];
RouteTable.Routes.CopyTo(routesCopy, 0);
config.Routes.Clear();
config.Routes.MapHttpRoute(name: "AuthorizeWebApiRoute", routeTemplate: "authorize", defaults: new { controller = "My", action = "authorize" });
config.Routes.MapHttpRoute(name: "ReleaseWebApiRoute", routeTemplate: "release", defaults: new { controller = "My", action = "release" });
foreach (var route in routesCopy)
{
RouteTable.Routes.Add(route);
}
}
}
As long as "my" route mappings don't match the default mappings (I made them specific enough not to), I'm all good - I think...

Hierarchies in MVC3

Im working my way though an ASP.NET MVC tutorial and couldnt find the answer im looking for.
I understand that each controller class in the 'Controller' root folder is mapped to a Url, so:
****Controller Folder****
|- StoreController.cs
Maps to $url/Store
However, If I wish to creater a 'subfolder'
I.e. a Controller class located for $url/Store/Testing I cant seem to see how I go about it.
I tried deriving a class from StoreController.cs, but that didnt work.
URLs do not necessarily correspond to MVC application internal folder structure. You can use MVC routing tables to conceal the internal structure and redirect specific URLs to any controllers/actions you want. For example, you can create a TestingController.cs class in the Controllers folder and use this route in Global.asax:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Store-Testing", // Route name
"Store/Testing/{action}/{id}", // URL with parameters
new { controller = "Testing", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
In this case, a request to http://[domain]/Store/Testing will be handled by TestingController.
That url would with the default route point to an action called Testing, within the Store controller.
You can however create your own custom routes in your global.asax file.

Incorrect Routing is ASP.NET MVC

I don't know why I have such problems with ASP.NET MVC routing. I wish there was a tool that showed me which routes I had currently setup. Regardless,
In my global.asax.cs file I have the following:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"SignUp", // Route name
"account/{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Register" } // Parameter defaults
);
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
I have the following defined in HomeController.cs
public ActionResult Register()
{
return View();
}
I was expecting to be able to access this page by visiting /account/register in my browser. However, I continue to get a 404. What am I doing wrong?
/Account/Register matches your first route.
The word Register is matched to the {controller}, so it looks for a controller named RegisterController.
replace
routes.MapRoute(
"SignUp", // Route name
"account/{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Register" } // Parameter defaults
);
with
routes.MapRoute(
"SignUp", // Route name
"account/{action}", // URL with parameters
new { controller = "Home" } // Parameter defaults
);
This will mean /account/register will route to the Register action on the Home controller. It will also mean that action links and other links you generate via #Html.ActionLink("Register", "Register", "Home") will generate the URL /account/register
Think of the 'URL with paramters' as a pattern that the URL will be matched against.
The problem with your original route map is that it is looking for a URL like this /account/controllername/actionname. So, when you go /account/register - it is taking register as the controller name, and taking the default action name (in this case register) - and as the 'register' action does not exist in the 'register' controller - you are getting a 404.
UPDATED
I updated my suggested route as per Robert's comments.
It is also worth noting, as Robert states, that this whole thing could be made more simple by making a 'Account' controller, and moving the 'Register ' action there. Then you could delete the 'SignUp' route, and just use default routing. If you thought about it, you'd agree that this would be a better place for a 'Register' action than the 'Home' controller.
Try using this nugget package http://nuget.org/packages/Glimpse.Mvc3
You can find more info about glimpse on http://getglimpse.com/

Controller not being properly activated

I have added a controller to my project named UserManager (automatically generated from the ado.net framework)
When I start the application, attempts to navigate to http://server/UserManager/ are met with a 404 error, but if I go to http://server/UserManager/Index the action is found and executes properly.
Is this a case of the controller not being called or is it just not treating index as the default action. Where are these properties set?
UPDATE
It seems that the problem derived from the fact that the default route is set to
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Permits", action = "ListApplications", id = UrlParameter.Optional }
This conflicts with the naming scheme for Usermanager (where the default is Index)
I struggled with ohow to add alternate routes that provided for default actions, but eventually figured out that the order of route addition determines which route takes the request (the earlier the route is added, the more chances it has to meet the criteria.)
Thanks
You need to ensure that the default route mapping specifies "Index" as the default action in your global.asax file.
Check that you have the following setting in your global.asax file:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
IN REPLY TO YOUR COMMENT:
Only by way of adding new route mappings. You also need to be aware that the first matching route mapping will be applied, so the order you specify the mappings in Global.asax is crucial.
For example, we wanted our FAQ controller to work with a URL http://domain/faq/{id} without the action specified in the URL, so we declared the following mapping before the default:
routes.MapRoute("Faq", "Faq/{id}", new { controller = "Faq", action = "Answer" });
In Global.asax.cs, check the default route is set up:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}", new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional}
);
Also, check that the controller is called UserManagerController, and derives from Controller

Area can't load in MVC - the resource cannot be found

I have a problem. I have a area in MVC 3 called Page that works as it should.
I just added a new Area called Media and now I get "the resource cannot be found" for that new area. I am going crazy, since it looks exactly like the PageArea that works.
Here is the MediaAreaRegistration.cs
public override string AreaName
{
get
{
return "Media";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Media_default",
"{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
}
Here is my global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
I am trying to access via localhost/media/, but I am just getting "the resource cannot be found".
Any ideas?
Check the Namespace of the Controller;
In my case; the default route was:
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
defaults: new {controller = "Home", action = "Index", AreaName="Admin", id = UrlParameter.Optional },
namespaces: new[] { "MyApp.Admin.Controllers"}
);
But when I was create the controller, the MVC automatically set "MyApp.WebUI.Areas.Admin.Controllers" as the namespace of the new Controller; I Changed the namespace to what I defined in default route as "MyApp.Admin.Controllers" and application works fine.
Typically, when you create an area, you will get a somewhat different default route than what is in global.asax. For example, I created a Media area in an MVC3 project, and the default route looks like this:
context.MapRoute(
"Media_default",
"Media/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
Routes in areas are really no different than routes defined in your global asax, except that they look for controllers in a different namespace. Also, they are loaded before the routes in your global.asax. You can see this because in Application_Start, RegisterAllAreas is invoked before RegisterRoutes.
Typically, this is the URL schema for root controllers with routes defined in your global.asax:
base/ControllerAName/Action1Name
base/ControllerAName/Action2Name
base/ControllerBName/Action6Name
...and so on. This is the "convention" you get with MVC out of the box. Look closely, and you will see that this pattern matches the base route definition in your global asax:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index",
id = UrlParameter.Optional } // Parameter defaults
);
On the other hand, the convention when you use areas is that your "conventional" URL schema will look like this:
base/AreaName/ControllerAName/Action1Name
base/AreaName/ControllerAName/Action2Name
base/AreaName/ControllerBName/Action6Name
Notice the difference? This is why your default route definition in the area registration looks like this: "Media/{controller}/{action}/{id}"
With all of this said, there is nothing stopping you from deviating from the conventions. It sounds like you want to have an area named Media, and a URL base/media that goes to some action method on some controller in the area. If that is correct, try this -- remembering to put your more specific route before the default route generated by MVC:
context.MapRoute(null,
"media",
new { action = "Index", controller = "Media" }
);
context.MapRoute(
"Media_default",
"Media/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
This means that MVC will match base/media to the Index action method on the MediaController in your Media area, since that route is defined first.
Also, when you create a new area, don't change any namespaces. This will only cause you problems.
Another tip is to not give route names to your routes. Notice how I passed null as the first argument. This is considered good practice -- accessing routes by name can get very messy.
I suggest you try starting a new project, or creating a new area, and trying these suggestions. Grasping routes coming from webforms can be tricky, but once you get a handle on it, I think you will find it superior to the URL-TO-FILE mapping in webforms.
In my case, someone added routes.Clear() in RouteConfig.cs, before any area ever existed. But now I added an area, this was erasing all its routes.

Resources