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.
Related
I am new in c# mvc, and I am trying to make a route with multiple parameters that looks like this:
controller/action/parameterOne/parameterTwo
but in some cases I'm gonna just use one of them so the route will look like this:
controller/action/parameterOne
here is my RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name:"Default2",
url: "{controller}/{action}/{category}/{id}",
defaults: new { controller = "Home", action = "Index", category = UrlParameter.Optional, id = UrlParameter.Optional }
);
}
now in my controller's action I need to check if there is only one parameter or two so I can return a different view for each condition, here is the controller:
[HttpGet]
public ActionResult someAction(string category, string id)
{
if (String.IsNullOrWhiteSpace(id))
{
return View("viewOne");
}
else
{
return View("ViewTwo");
}
}
the problem is that the if statement is not full working? because if the conditoin is this: String.IsNullOrWhiteSpace(id)
and if I write controller/action/parameterOne this return the ViewOne
but if I write controller/action/parameterOne/parameterTwo also return the ViewOne
but now if a invert the condition and I write !String.IsNullOrWhiteSpace(id) both urls return ViewTwo.
So does any one have any idea why is that happening?
Do you have any objection to only using the default route? The only drawback that comes to mind is if you really want your url to look a certain way in which case you may need to define multiple routes as youre trying to do. However, the following should work with only the default route:
//note controller actions will default to HttpGet if no data annotation is explicitly supplied. Also, action names generally begin uppercase by convention
public ActionResult SomeAction(string category, string id = null)
{
if (String.IsNullOrWhiteSpace(id))
{
return View("viewOne");
}
else
{
return View("ViewTwo");
}
}
the various requests you mentioned would look like:
www.myhost.com/controller/someaction?category=parameterOne&id=parameterTwo
or
www.myhost.com/controller/someaction?category=parameterOne
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.
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.
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 = "") {
// ...
}
}
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" }
);