MVC 5 routing with 1 parameter - c#

I want the url to be
domain.com/doc/{project}
I have a controller called Projects or "ProjectsController.cs."
This controller has an action (ActionResult) I want to reach with my custom url route called "DocumentationIndex."
DocumentationIndex does have a parameter. It's a simple string parameter.
The following continues to give me a 404.
routes.MapRoute(
name: "Documentation",
url: "doc/{project}",
defaults: new { controller = "Projects", action = "DocumentationIndex", project = "" }
);
Can anyone point me in the right direction?

You can use attribute routing to define this within the controller:
public class ProjectsController
{
[Route("doc/{*project}")]
public ActionResult DocumentationIndex(string project = "")
{ // ... }
}
You'll need to ensure attribute routing is enabled in your Route Config:
routes.MapMvcAttributeRoutes();
The * in the {*projects} will ensure that the route parameter matches the rest of your URL, even if it contains special characters.

Try this:
routes.MapRoute(
"Documentation",
"doc/{project}",
new { controller = "Projects", action = "DocumentationIndex" }
);
Your controller code:
public class ProjectsController ... {
//
public ActionResult DocumentationIndex(string project = "") {
// ...
}
}

Related

Select route depends on URL parameter. Constraints

First of all, I need to say that I'm using T4MVC in my project. I have one method for two routes :
public virtual ActionResult List(string slug, string category, String selectedFilters)
Routes:
routes.MapRoute("ProductOnSale", "products/{slug}/{category}/onsale", MVC.Product.List());
routes.MapRoute("ProudctsList", "products/{slug}/{category}/{selectedFilters}", MVC.Product.List()
.AddRouteValue("selectedFilters", ""));
As you can see, this is only one ActionResult for two routes. They have a different url. Example for the first route:
products/living-room-furniture/sofas/sectional-sofa
Example for the second route:
products/living-room-furniture/living-room-tables/onsale
This piece should say that I came from the another page. I need to add Boolean parameter to my method List(string slug, string category, String selectedFilters, bool onsale) and, depends on that, choose route. Is it possible to do using constraints? May anyone provide an example how to do it in this case?
I'm not sure if I understand your question correctly. Two cases I come accross might help you.
CASE 1 : redirect to another URL depending on the URL used to access the page.
STEP 1: Create an MVCRouteHandler
public class LandingPageRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext Context)
{
if ( Context.HttpContext.Request.Url.DnsSafeHost.ToLower().Contains("abc"))
{
Context.RouteData.Values["controller"] = "LandingPage";
Context.RouteData.Values["action"] = "Index";
Context.RouteData.Values["id"] = "abc";
}
return base.GetHttpHandler(Context);
}
}
STEP 2: Add a RouteHandler
routes.MapRoute(
name: "Landingpage",
url: "Landingpage/{id}/{*dummy}",
defaults: new { controller = "Landingpage", action = "Index" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
).RouteHandler = new LandingPageRouteHandler();
CASE 2 : Add a property to a controller and view depending on the url used
All controlles in my case derive from a BaseController class. In the BaseController I have :
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
//Set TenantByDomain
var DnsSafeHost = filterContext.HttpContext.Request.Url.DnsSafeHost;
int? TenantByDomain = null;
if (db.Tenants.Any(x => x.DnsDomains.Contains(DnsSafeHost)))
{
TenantByDomain = db.Tenants.First(x => x.DnsDomains.Contains(DnsSafeHost)).Id;
}
((BaseController)(filterContext.Controller)).TenantByDomain = TenantByDomain;
filterContext.Controller.ViewBag.TenantByDomain = TenantByDomain;
}
Applied to your Question.
Using the routehandler you could add an extra property indicating the original route taken and redirect both to a 3th url (! the user does not see this new url).
In the OnActionExecuting do something with the extra routevalue so that the handling can be done as desired.

MVC Default route with nullable id not working as expected

A simple routing scenario is not working for me.
my route registration looks like this
context.MapRoute(
"Users_default",
"Users/{controller}/{action}/{id}",
new { action = "Index", id= UrlParameter.Optional });
and i am expecting it to honor the requests for
users/profile/
users/profile/1
users/profile/2
with the following controller
public class ProfileController : Controller
{
public ActionResult Index(int? id)
{
var user = id == null ? (UserModel)HttpContext.Session["CurrentUser"] : userManager.GetUserById((int)id);
return View(user);
}
}
it works for users/profile but not for users/profile/1
i've tried few different things but i know the answer must be simple, its just my lack of knowledge, what am i missing here.
i dont want index to appear. i want to use the same method for both users/profile/1 and users/profile/
Then don't put action into your URL.
context.MapRoute(
"Users_default",
"Users/{controller}/{id}",
new { action = "Index", id= UrlParameter.Optional });
The route you have defined will not allow index to be optional because it is followed by another parameter (in this case "id"). Only the last parameter can be optional on all but the default route.
This is because your route interprets as:
{controller: "profile", action: "1"}.
You need to point you details action url explicit, something like this:
users/profile/index/1
You can use Attribute routing
The code would look like
public class ProfileController : Controller
{
[Route("users/profile/{id}")]
public ActionResult Index(int? id)
{
var user = id == null ? (UserModel)HttpContext.Session["CurrentUser"] : userManager.GetUserById((int)id);
return View();
}
}
And you have to modify your RouteConfig
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// This will enable attribute routing in your project
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
So now you can use users/profile for your default behaviour and users/profile/ for a specific profile.

Passing two parameters to controller with #Hml.ActionLink, but the second parameter value is always null

In an ASP.Net MVC 5 application, I use the #Hml.ActionLink helper to call an action on a controller, where I need to pass two parameters. However, the second parameter always ends up with a null value.
Here is the view code, with ActionLink:
#Html.ActionLink(
linkText: "Remove",
actionName: "DeleteItemTest",
controllerName: "Scales",
routeValues: new
{
itemID = 1,
scaleID = 2
},
htmlAttributes: null
)
Here is the controller code:
public ActionResult DeleteItemTest(int? itemID, int? scaleID)
{
//...doing something here....
return View();
}
This is the html that ends up on the page:
Remove
In my controller, I then end up with a value of 1 for "itemID", and null for "scaleID". What am I doing wrong?
Update - adding Route configuration based on request:
public static class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.AppendTrailingSlash = true;
routes.LowercaseUrls = true;
// Ignore .axd files.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// Ignore everything in the Content folder.
routes.IgnoreRoute("Content/{*pathInfo}");
// Ignore everything in the Scripts folder.
routes.IgnoreRoute("Scripts/{*pathInfo}");
// Ignore the Forbidden.html file.
routes.IgnoreRoute("Error/Forbidden.html");
// Ignore the GatewayTimeout.html file.
routes.IgnoreRoute("Error/GatewayTimeout.html");
// Ignore the ServiceUnavailable.html file.
routes.IgnoreRoute("Error/ServiceUnavailable.html");
// Ignore the humans.txt file.
routes.IgnoreRoute("humans.txt");
// Enable attribute routing.
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}
I see you're using attribute routing and MapMvcAttributeRoutes; do you have this route mapped? If not, the default route will take precedence and take only the first parameter as the ID.
You'd need to add in a route that expects both parameters.
Something like this would be slapped onto the controller action:
[Route("{itemID:int}/{scaleID:int}", Name = "DeleteItemTest")]
public ActionResult DeleteItemTest(int? itemID, int? scaleID)
Please note that this is not exact code, just something to work from.

ASP.NET MVC Page-URL and Controller/Action Routing

I have problems building an ASP.NET MVC page which allows two sorts of routing.
I have a database where all pages are stored with an url-path like: /Site1/Site2/Site3
i tried to use an IRouteConstraint in my first route, to check wether the requested
site is a site from my database (permalink).
In the second case, i want to use the default asp.net mvc {controller}/{action} functionality, for providing simple acces from an *.cshtml.
Now i don't know if this is the best way. Furthermore i have the problem, how to root with the IRouteContraint.
Does anyone have any experiance with this?
I'm using asp.net mvc 5.
Problem solved, final solution:
Adding this two routes:
routes.MapRoute(
"FriendlyUrlRoute",
"{*FriendlyUrl}"
).RouteHandler = new FriendlyUrlRouteHandler();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Page", action = "Load", id = UrlParameter.Optional },
namespaces: controllerNamespaces.ToArray()
);
My own Route-Handler:
public class FriendlyUrlRouteHandler : System.Web.Mvc.MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
var friendlyUrl = (string)requestContext.RouteData.Values["FriendlyUrl"];
WebPageObject page = null;
if (!string.IsNullOrEmpty(friendlyUrl))
{
page = PageManager.Singleton.GetPage(friendlyUrl);
}
if (page == null)
{
page = PageManager.Singleton.GetStartPage();
}
// Request valid Controller and Action-Name
string controllerName = String.IsNullOrEmpty(page.ControllerName) ? "Page" : page.ControllerName;
string actionName = String.IsNullOrEmpty(page.ActionName) ? "Load" : page.ActionName;
requestContext.RouteData.Values["controller"] = controllerName;
requestContext.RouteData.Values["action"] = actionName;
requestContext.RouteData.Values["id"] = page;
return base.GetHttpHandler(requestContext);
}
}
You can use attribute routing which is in MVC 5 and combine attribute routing with convention-based routing to check the condition that you want on controller class or action methods.
And you could make the constraint yourself to use it on the action methods like this:
public class ValuesConstraint : IRouteConstraint
{
private readonly string[] validOptions;
public ValuesConstraint(string options)
{
validOptions = options.Split('|');
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
object value;
if (values.TryGetValue(parameterName, out value) && value != null)
{
return validOptions.Contains(value.ToString(), StringComparer.OrdinalIgnoreCase);
}
return false;
}
}
To use attribute routing you just need to call MapMvcAttributeRoutes during configuration and call the normal convention routing afterwards. also you should add your constraint before map the attributes, like the code below:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var constraintsResolver = new DefaultInlineConstraintResolver();
constraintsResolver.ConstraintMap.Add("values", typeof(ValuesConstraint));
routes.MapMvcAttributeRoutes(constraintsResolver);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Now on your controller class you can check the route and decide what to do with different urls like below:
for example: // /mysite/Site1 and /mysite/Site2 but not /mysite/Site3
[Route("mysite/{site:values(Site1|Site2)}")]
public ActionResult Show(string site)
{
return Content("from my database " + site);
}
And you could do all kind of checking just on you controller class as well.
I hope this gives you a bit of clue to achieve the thing that you want.

Redirect to another controller/Action not registered in route

iam having controller like below which is not registered in routes table
public class InternalController : Controller
{
/*this controller is not registered in routes table*/
public ActionResult Foo()
{
return Content("Text from foo");
}
}
From another controller which is registered in Routes table i want to call/redirect action of previous controller, one which is not registered in routes table.
public class AjaxController : Controller
{
/*this controller is registered in routes table*/
public ActionResult Foo()
{
/*FROM HERE HOW DO I RETURN CONTENTS OF
controller=InternalController, action = Foo
*/
/*
i tried below piece of code but that doesnt seem to work
*/
return RedirectToAction("Foo", "InternalController ");
}
}
Defined Routes (only one item added)
public void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute("Ajax","ajax/{action}",new {
controller="Ajax",
action="Index"
});
}
If you are choosing not to register a route... then you probably have the file/controller in a specific location that will not change.
In that event, just use the "Redirect" method, instead of "RedirectToAction".
For example:
return Redirect("~/Internal/Foo");
Now that you have shown your routes definition, it is obvious that you can never invoke any other controller than AjaxController. You simply forbid them in your routes, so InternalController could never be served. You will have to change your route definition.
Depending on what you want to achieve and how you want your urls to look like you have a couple of possibilities:
Leave the default route
Modify your existing route definition like so:
routes.MapRoute(
"Ajax",
"{controller}/{action}",
new { controller = "Ajax", action = "Index" }
);
You can create RedirectController for redirecting more Url and pages:
public class RedirectController : Controller
{
public ActionResult Index()
{
var rd = this.RouteData.Values;
string controller = rd["controller2"] as string;
string action = rd["action2"] as string;
rd.Remove("controller2");
rd.Remove("action2");
rd.Remove("controller");
rd.Remove("action");
return RedirectToActionPermanent(action, controller, rd);
}
}
And then you can define redirect from old url in routing tables:
routes.MapRoute(
null, // Name
"ajax/foo",
new { controller = "Redirect",
action = "Index",
controller2 = "InternalController",
action2 = "Foo"}
);
This pattern is also useful if you redirect old url to new one. For example:
routes.MapRoute(
null, // Name
"default.aspx", // redirect from old ASP.NET
new { controller = "Redirect",
action = "Index",
controller2 = "Home",
action2 = "Index" }
);

Categories

Resources