Removing /Index from end of browser URL - c#

I'm a bit rusty on my MVC routing.
I'm using a default route of
routes.MapRoute(
"Default",
"{controller}/{action}",
new
{
controller = "Default",
action = "Index",
});
I have a controller:
public class SomeController : Controller
{
public async Task<ActionResult> Index()
So, typically, this can be hit with either http://mydomain/Some/Index -or- http://mydomain/Some
On this particular controller, I'd like to either deny (404) ~/Some/Index or simply redirect ~/Some/Index to ~/Some.
This is relatively easy to accomplish in the controller:
public async Task<ActionResult> Index()
{
var p = Request.Url.Segments;
var last = p.Last();
if(string.Equals(last, "index", StringComparison.InvariantCultureIgnoreCase))
{
return RedirectToActionPermanent("Index");
}
but now it seems like the controller is getting polluted by cross-cutting concerns. Is there a cleaner way?

Yes there are much cleaner ways -
Option 1 - You can use HTtpModule for Permanant redirection
Option 2 - Use UrlRewriter
Option 3 - You can also implement an Action Filter in which you can redirect

So, if I add another route above the default route that dumps this specific url onto a non-existent controller/action, then I get a lovely 404. Perfect.
routes.MapRoute(
"SomeIndexDeny",
"Some/Index",
new
{
controller = "NonExistentController",
action = "NonExistentMethod"
});
routes.MapRoute(
"Default",
"{controller}/{action}",
new
{
controller = "Default",
action = "Index",
});

Related

How can I implement routing for an extended routes in the RouteConfig file without creating a route error

I implemented a logout link in an ASP.NET MVC 5 application (C#), and it works perfectly with the current routing of my application:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Login", id = UrlParameter.Optional });
When I log in, I see the logout button and it works seamlessly.
However, I have other areas of the app that are designated for registered users (for which I have not considered the routing), and after going to an area designed for authorized users and I attempt to log out, I get the following path returned when the browser displays an error:
/Managers/Home/LogOff
I have a home controller with an action link of Logoff, but I'm uncertain how to represent my Managers folder (along with others on that level). And I could not find (and really, I have not searched exhaustively) readily available documentation of routing beyond what I implement in my routes.MapRoute file (seen above). I would appreciate it if someone can steer me in the right direction or tell me the correct pattern to use. I can't imagine the issue being anything other than routing. Thanks!
RouteConfig.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace HRCoreUS
{
using System.Web.UI.WebControls;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Login", id = UrlParameter.Optional });
}
}
}
I see two options for you to define a route that suits you.
Fist one, if you want to log off all users from a specific folder, you can use a route like the following one. The keyword 'allusers' prevents you to have a conflict with your Default route. This also make your route more 'readable'.
config.Routes.MapHttpRoute(
"LogOffAllUserFromFolder",
"{controller}/{action}/allusers/{folder}",
new { controller = "Home", action = "Logoff", folder = RouteParameter.Optional });
This endpoint would then looks like :
/home/logoff/allusers/Managers
Second one, if you want to log off a specific user (for example user with id 124) from a 'Managers' folder, you can try the following route that won't conflict with Default since there are 4 arguments
config.Routes.MapHttpRoute(
"LogOffSpecificUserFromFolder",
"{controller}/{action}/{id}/{folder}",
new { controller = "Home", action = "Logoff", id = RouteParameter.Optional, folder = RouteParameter.Optional },
new { id = #"\d+" });
The endpoint would then looks like
/home/logoff/124/Managers
As you're id is an int I suppose, you can tell it by adding that to your route definition
new { id = #"\d+" }
And as {folder} is a string you don't need any other definition as it is the default type.
If I missunderstand your point, I hope this will gives you tips on how to declare MVC routes.
Take care.
If you want to use Attribute routing inside your Controllers folder Simply create a ManagersController
[RoutePrefix("managers")]
public class ManagerController
Then simply add a [Route("")] attribute on every function inside the controller.
Depending on any URL parameters you need define them in the Route attribute.
[HttpGet, Route("logoff}")]
public HttpResponseMessage LogOff() //route would be Managers/logoff
[HttpGet, Route("{id:int/view-profile}")]
public HttpResponseMessage ViewProfile(int id) //route would be Managers/4/view-profile
[HttpPost, Route("{id:guid/save-profile}")]
public HttpResponseMessage ViewProfile(Guid id) //route would be Managers/405750C8-0B3F-4EF2-A2A2-17A1EFBCDA39/save-profile
More info on attributes -
https://blogs.msdn.microsoft.com/webdev/2013/10/17/attribute-routing-in-asp-net-mvc-5/
or for general catchall maproute in Routeconfig you would want something like this:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
Make sure this is the last route defined in your RouteConfig.
Then inside a ManagersController.cs create a
public HttpResponseMessage LogOff()
This Route would be localhost.com/managers/logoff
But from how I interpreted your question it seems as if your Authentication logic is tied to a specific controller (Home)? You should have all that logic encapsulated in something like a AuthenticationController which could handle your login / logoff logic. This way its not dependent on anything else and you could have a route like this:
routes.MapRoute(
name: "Authentication",
url: "authentication/{action}",
defaults: new { controller = "Authentication", action = "Index"}
);
with a Login / LogOff Function. These would route to /authentication/login and /authentication/logoff

ASP.NET MVC redirect to action Redirect Loops

I am learning asp.net MVC and I was trying Redirect to action and I tried the following code and I keep getting Redirect Loop Error.
This is the Controller class in which I am getting an error
public class CuisineController : Controller
{
// GET: Cuisine
public ActionResult Search(string name)
{
return RedirectToAction("About","Cuisine");
}
public ActionResult Index()
{
return Content("This is Index");
}
public ActionResult About()
{
return Content("this is About");
}
}
I have also created a route of my own other than the Default route
routes.MapRoute(name: "Cuisine",
url: "cuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search", name = UrlParameter.Optional });
When I try to access the Cuisine controller it gives me a redirect loop error.
Thank you
In your routeConfig, you have a route defined for "cuisine/{name}" and it will be sent to the Search action method.
In your Search action method, You are redirecting to the About, Which is cuisine/About. This is matching with the route you defined cuisine/{name} so it will send the request to Search action again. The same process will keep running. That is why you are getting the redirect loop
You should either delete this specific route you defined or rename the url pattern for cusine search to prevent the redirect loop.
routes.MapRoute(name: "Cuisine",
url: "searchcuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search",
name = UrlParameter.Optional });
This should be pretty obvious:
routes.MapRoute(name: "Cuisine",
url: "cuisine/{name}",
defaults: new { controller = "Cuisine", action = "Search", name = UrlParameter.Optional })
Says all urls that start with cuisine/ use the Search method on the CuisineController.
/Cuisine/About starts with that url, so it willl always use the Search method.
It looks like you want to make {name} part of URL. You can use attribute routing instead of changing default routing, it does not have such global "destructive" effects (explained in other answers) at least, and looks it's what you are actually after :)
[Route("Search/{name}")]
public ActionResult Search(string name)
{
return RedirectToAction("About", "Home");
}
See more about attribute routing here (for this to work, don't forget to add routes.MapMvcAttributeRoutes() if it is not there yet).
You need to restore controller action pattern. Just change url: "cuisine/{name}" to url: "cuisine/{action}/{name}"

Poor Performance And Slow Website With MVC 5 Attribute Routing

I am developing a website on Azure, with mvc5. I use attribute routing, with routes and route prefix on controllers. I call with action.link helper. I did not name my routes.
I did the following on my route.config:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.LowercaseUrls = true;
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
My controllers are like:
[OutputCache(Duration = 600, Location = System.Web.UI.OutputCacheLocation.Client)]
[RoutePrefix("istanbul/kadikoy")]
[Route("{action=index}")]
public class KadikoyController : Controller
{
public ActionResult Index()
{
return View();
}
[Route("kadikoy-tarihi")]
public ActionResult KadikoyTarihi()
I have very very poor performance as server response time, i.e. 9.6s
If I comment out the attribute route codes, with default routing, I have 2.1 s server response time.
Thank you for your replies.
It turns out that the really expensive bit of this operation isn't mapping your attributed routes, it's that before that can happen MVC needs to create the ControllerFactory and retrieve all the Controller types. That process accounts for 1245 ms in my project, while the rest of the MapMvcAttributeRoutes() functions take about 45ms. My guess is that if you don't use the attribute routing the controllers are found as needed rather than all at once.

Issue routing default route in MVC with Attribute Routes

I am trying to find out why the routing of these two sittuations are not functioing. I want to conver this rout in the Routes.config file to a route attribute. It needs to be a default, because when you just load the site, without any url entered, it needs to hit /Home/Index action.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I added this to the Home controller for the actionResult Index(), but it didn't work. Said no document loaded.
[Route]
[Route("~/", Name = "default")]
public ActionResult Index()
{
var environmentsSharedAccounts = new List<EnvironmentSharedAccountVM>();
return View(environmentsSharedAccounts);
}
Why does this now not resolve. Thanks in advance. BTW. I am finding, that the books I have read, and the examples I have seen are seeming to be more compleicated to me then I thought I guess, because this routing has been giving me issues. I think I understand it, but it doesn't work how expected. Anyways....
Try
[Route("")]
public ActionResult Index()
....
Do you have this line on WebApiConfig.cs :
config.MapHttpAttributeRoutes();
?

How to set up routing so that Index does show?

So I know google can penalize a site if you have the same content on multiple urls... unfortunately, in MVC this is too common i can have example.com/, example.com/Home/ and example.com/Home/Index and all three urls would take me to the same page... so how do I make sure that whenever Index is in the url, that it redirects to the same without the Index and of course the same thing with the Home
Perhaps this little library may be useful for you.
This library is not very convinient in your case, but it should work.
var route = routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.Redirect(r => r.MapRoute("home_index", "/home/index")).To(route);
routes.Redirect(r => r.MapRoute("home", "/home")).To(route);
The way I handle this is for default pages like Index is to simply create an explicit route for only one of them. I.e. "example.com/People" would be the route for People/Index, and there would be no valid page at the url "/example.com/People/Index".
The Home example is unique in that it has potentially three different URLs. Again in this case I'd simply create a route for "example.com" for that Index action, and not support the other two urls. In other words, you would never link to the other forms of the URL, so their absence should never cause a problem.
We use a Nuget package called AttributeRouting to support this. When you specifiy a GET route for a page, it overrides the defaults for MVC.
Using AttributeRouting usually you'd map the index to [GET("")] but for the special case of Home where you also want to also support the root URL that omits the controller name , I think you'd also add an additional attribute with IsAbsoluteUrl:
public class HomeController : BaseController
{
[GET("")]
[GET("", IsAbsoluteUrl = true)]
public ActionResult Index()
{...
So I found a way to do it without any external Library...
In my RouteConfig I had to add these two routes at the top, just below the IgnoreRoute
routes.MapRoute(
"Root",
"Home/",
new { controller = "Redirect", action = "Home" }
);
routes.MapRoute(
"Index",
"{action}/Index",
new { controller = "Redirect", action = "Home" }
);
Then I had to create a new Controller called Redirect and I created a method for each of my other Controllers like this:
public class RedirectController : Controller
{
public ActionResult Home()
{
return RedirectPermanent("~/");
}
public ActionResult News()
{
return RedirectPermanent("~/News/");
}
public ActionResult ContactUs()
{
return RedirectPermanent("~/ContactUs/");
}
// A method for each of my Controllers
}
That's it, now my site looks legit. No more Home, no more Index in my URLs, this of course has the limitation of not being able to accept parameters to any of the Index methods of your Controllers though if it was really necessary, you should be able to tweak this to achieve what you want.
Just an FYI, if you want to pass an argument to your Index Action, then you can add a third route like this:
routes.MapRoute(
name: "ContactUs",
url: "ContactUs/{id}/{action}",
defaults: new { controller = "ContactUs", action = "Index", id = UrlParameter.Optional }
);
This will create a URL like this: /ContactUs/14

Categories

Resources