asp.net mvc: read route param inside controller - c#

I'm trying to read the a parameter that I've defined in a route from inside the controller.
The route:
routes.MapRoute(
"BusinessVoice", // Route name
"business/{controller}/{action}/{id}", // URL with parameters
new { controller = "Voice", action = "Index",
id = UrlParameter.Optional, locale = "business" } // Parameter defaults
);
From inside the controller I'd like to be able to read the route parameter locale, but have not idea where to look for it.
The controller:
namespace www.WebUI.Controllers
{
public class VoiceController : Controller
{
public VoiceController()
{
... want to read the locale param here
}
public ViewResult Index(string locale)
{
return View();
}
}
}
Any help is appreciated!

Dave,
This is from my basecontroller but you should be able to do exactly the same from a top level one too:
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
var locale = requestContext.RouteData.Values["locale"].ToString() ?? System.Globalization.CultureInfo.CurrentUICulture.TwoLetterISOLanguageName;
base.Initialize(requestContext);
}
good luck
jim

public VoiceController()
{
var locale = this.RouteData.Values["locale"];
}

Related

MVC Action Attribute that checks parameter has value?

I have a handful of Actions in my ASP.NET MVC site that have one parameter. The first lines of code on all these actions checks that the parameter is not null, and if it is, redirect to a page that allows them to choose a value for the parameter.
For example:
public ActionResult Summary(string client)
{
if (String.IsNullOrEmpty(client))
return RedirectToAction("Select");
return View();
}
I'd like to create an attribute that does something like the above code, so I don't have to repeat it in every action. Something like:
[ClientRequired]
public ActionResult Summary(string client)
{
return View();
}
And then the ClientRequiredAttribute would check the value of the client parameter, and if it's empty/null, redirect to the select client page. Is such an attribute possible?
-shnar
Yes, it is possible.
It would be something like this:
public class ClientRequiredAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
object parameter = null;
filterContext.ActionParameters.TryGetValue("client", out parameter);
var client = parameter as string;
if (string.IsNullOrEmpty(client))
{
var urlHelper = new UrlHelper(filterContext.Controller.ControllerContext.RequestContext);
var url = urlHelper.Action("Select", "ControllerName");
filterContext.Result = new RedirectResult(url);
}
}
}
PS: Not tested, but it should work.
In ASP.NET MVC 5 you can use attribute routing.
http://blogs.msdn.com/b/webdev/archive/2013/10/17/attribute-routing-in-asp-net-mvc-5.aspx
public class ReviewsController : Controller
{
// eg: /reviews
[Route("reviews")]
public ActionResult Index() { ... }
// eg: /reviews/5
[Route("reviews/{reviewId}")]
public ActionResult Show(int reviewId) { ... }
// eg: /reviews/5/edit
[Route("reviews/{reviewId}/edit")]
public ActionResult Edit(int reviewId) { ... }
}

Route to user's page under the dynamic username path in MVC4

I have some dynamic user route like
routes.MapRoute(
"UserNames", // Route name
"{username}", // URL with parameters
new { controller = "Home", action = "UserName" });
and under the HomeController.cs
public ActionResult UserName(string username)
{
ViewBag.Message = username;
return RedirectToAction("Register","Account"); // Test...
}
It is working fine.
But what I need is to get working the URL like
http:\\mywebsite.com\UserNameBob\MyGallery\1
http:\\mywebsite.com\UserNameBob\Profile
http:\\mywebsite.com\UserNameBob\MyFriends
How do I can archive it?
Any clu?
Thank you!!!
Do you mean something like this:
routes.MapRoute(
"UserNames", // Route name
"{username}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "UserName", id = UrlParameter.Optional });
And then in HomeController you put actions like these:
public ActionResult MyGallery(string username, int id) {
// code
}
public ActionResult Profile(string username) {
// code
}
EDIT: Of course, if the gallery ID is not an int, just use string or whatever is appropriate.
Look for URL Rewriting in ASP.NET to handle the dynamic parameters while routing.

MVC 3 routing for unknown urls

I want to be able to handle any url that s requested via some controller.
foo.com/a
foo.com/abcd
foo.com/x1
for foo.com/a
I want to process it with
UrlHandlerController with Process(string url) method.
How should i add a routing rule to be able to do this?
Any ideas?
Create a new custom route and use Phill Haack's Route Debugger to test your routes:
routes.MapRoute(
"customroute",
"{url}",
new { controller = "UrlHandler",
action = "Process",
url = ""
}
);
Controller:
public class UrlHandlerController : Controller
{
[HttpGet]
public ActionResult Process(string url)
{
return View();
/* or */
if(url == "something"){
return View("SomethingView");
}
else if(url == "somethingelse"){
return View("SomethingElseView");
}
}
}
Darth, see if this route helps:
routes.MapRoute(
"CustomRoute", // Route name
"{url}", //Route formation
new { controller = "UrlHandler", action = "Process" }, // Where to send it
new { keyWord = #"\S+" } // Regex to identify the argument
);
Regards.

Getting url path from ASP.net MVC route

I have a controller that looks like this:
public class PageController : Controller
{
public ActionResult Render(string url)
{
//this is just for testing!
return Content("url was " + url);
}
}
I'm trying to pass in the value of the url into the controller. For example:
http://www.site.com/products/something/else
Would pass "products/something/else" into my Render action of the PageController.
This is because we are using "products/something/else" as a unique key for a record in the database (legacy system, don't ask)
So, my resultant query would be something along the lines of this:
select * from foo where urlKey = 'products/something/else'
So far I have this in my RegisterRoutes section on Global.asax:
routes.MapRoute("pages", "{*url}", new { controller = "Page", action = "Render", url="/" });
But this isn't working as expected...
By visiting www.site.com/products/something/else, the value passed into the controller is "home/index/0"
The only route defined in RegisterRoutes is that described in the question.
The below class matches every route but you can modify as per your needs.
public class LegacyRoute : RouteBase
{
public override RouteData GetRouteData(HttpContextBase httpContext)
{
RouteData result = null;
string url = httpContext.Request.RawUrl.Substring(1);
result = new RouteData(this, new MvcRouteHandler());
result.Values.Add("controller", "Page");
result.Values.Add("action", "Render");
result.Values.Add("url", url);
return result;
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
return null;
}
}
In Global.asax.cs
routes.Add(new LegacyRoute());
Hope this helps, one of our routes does something similar and this is the code:
routes.MapRoute(
name: "Standard",
url: "{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional, action = ControllersAndActions.TypicalController.IndexAction, page = 1 },
constraints: new
{
controller = ControllersAndActions.ControllerConstraintExpression
}
);

MVC 2.0, StructureMap, Areas, and Duplicate Controller Names

I have a bit of a problem. I have an area called Framed. This area has a home controller. The default for the site also has a home controller.
What I'm trying to do with this is have a version of each controller/action that is suitable for an IFrame, and a version that is the normal site. I do this through Master pages, and the site masterpage has many different content place holders than the framed version. For this reason I can't just swap the master page in and out. For example, http://example.com/Framed/Account/Index will show a very basic version with just your account info for use in an external site. http://example.com/Account/Index will show the same data, but inside the default site.
My IoC container is structuremap. So, I found http://odetocode.com/Blogs/scott/archive/2009/10/19/mvc-2-areas-and-containers.aspx and http://odetocode.com/Blogs/scott/archive/2009/10/13/asp-net-mvc2-preview-2-areas-and-routes.aspx. Here's my current setup.
Structuremap Init
ObjectFactory.Initialize(x =>
{
x.AddRegistry(new ApplicationRegistry());
x.Scan(s =>
{
s.AssembliesFromPath(HttpRuntime.BinDirectory);
s.AddAllTypesOf<IController>()
.NameBy(type => type.Namespace + "." + type.Name.Replace("Controller", ""));
});
});
The problem here that I found through debugging is that because the controllers have the same name (HomeController), it only registers the first one, which is the default home controller. I got creative and appended the namespace so that it would register all of my controllers.
Default Route
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { area = "", controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new[] { "MySite.Controllers" }
);
Area route
context.MapRoute(
"Framed_default",
"Framed/{controller}/{action}/{id}",
new { area = "Framed", controller = "Home", action = "Index", id = UrlParameter.Optional },
new string[] { "MySite.Areas.Framed.Controllers" }
);
As recommended by Phil Haack, I am using the namespaces as the 4th parameter
app start, just to prove the order of initialization
protected void Application_Start()
{
InitializeControllerFactory();
AreaRegistration.RegisterAllAreas();
RouteConfiguration.RegisterRoutes();
}
Controller Factory
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController result = null;
if (controllerType != null)
{
result = ObjectFactory.GetInstance(controllerType)
as IController;
}
return result;
}
So, when I hit /Home/Index, it passes in the correct controller type. When I hit /Framed/Home/Index, controllerType is null, which errors because no controller is returned.
It's as if MVC is ignoring my area altogether. What's going on here? What am I doing wrong?
In case anyone tries to do something similar, I used the idea from this post: Categories of controllers in MVC Routing? (Duplicate Controller names in separate Namespaces) I had to dump using areas altogether and implement something myself.
I have Controllers/HomeController.cs and Controllers/Framed/HomeController.cs
I have a class ControllerBase which all controllers in /Controllers inherit from. I have AreaController which inherits from ControllerBase which all controllers in /Controllers/Framed extend from.
Here's my Area Controller class
public class AreaController : ControllerBase
{
private string Area
{
get
{
return this.GetType().Namespace.Replace("MySite.Controllers.", "");
}
}
protected override ViewResult View(string viewName, string masterName, object model)
{
string controller = this.ControllerContext.RequestContext.RouteData.Values["controller"].ToString();
if (String.IsNullOrEmpty(viewName))
viewName = this.ControllerContext.RequestContext.RouteData.Values["action"].ToString();
return base.View(String.Format("~/Views/{0}/{1}/{2}.aspx", Area, controller, viewName), masterName, model);
}
protected override PartialViewResult PartialView(string viewName, object model)
{
string controller = this.ControllerContext.RequestContext.RouteData.Values["controller"].ToString();
if (String.IsNullOrEmpty(viewName))
viewName = this.ControllerContext.RequestContext.RouteData.Values["action"].ToString();
PartialViewResult result = null;
result = base.PartialView(String.Format("~/Views/{0}/{1}/{2}.aspx", Area, controller, viewName), model);
if (result != null)
return result;
result = base.PartialView(String.Format("~/Views/{0}/{1}/{2}.ascx", Area, controller, viewName), model);
if (result != null)
return result;
result = base.PartialView(viewName, model);
return result;
}
}
I had to override the view and partialview methods. This way, the controllers in my "area" can use the default methods for views and partials and support the added folder structures.
As for the Views, I have Views/Home/Index.aspx and Views/Framed/Home/Index.aspx. I use the routing as shown in the post, but here's how mine looks for reference:
var testNamespace = new RouteValueDictionary();
testNamespace.Add("namespaces", new HashSet<string>(new string[]
{
"MySite.Controllers.Framed"
}));
//for some reason we need to delare the empty version to support /framed when it does not have a controller or action
routes.Add("FramedEmpty", new Route("Framed", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}),
DataTokens = testNamespace
});
routes.Add("FramedDefault", new Route("Framed/{controller}/{action}/{id}", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
//controller = "Home",
action = "Index",
id = UrlParameter.Optional
}),
DataTokens = testNamespace
});
var defaultNamespace = new RouteValueDictionary();
defaultNamespace.Add("namespaces", new HashSet<string>(new string[]
{
"MySite.Controllers"
}));
routes.Add("Default", new Route("{controller}/{action}/{id}", new MvcRouteHandler())
{
Defaults = new RouteValueDictionary(new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
}),
DataTokens = defaultNamespace
});
Now I can go /Home/Index or /Framed/Home/Index on the same site and get two different views with a shared control. Ideally I'd like one controller to return one of 2 views, but I have no idea how to make that work without 2 controllers.
I had a similar issue using Structuremap with Areas. I had an Area named Admin and whenever you tried to go to /admin it would get to the StructureMap Controller Factory with a null controller type.
I fixed it by following this blog post:
http://stephenwalther.com/blog/archive/2008/08/07/asp-net-mvc-tip-30-create-custom-route-constraints.aspx
Had to add a constraint on the default route to not match if the controller was admin.
Here's my default route definition:
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "MyController", action = "AnAction", id = UrlParameter.Optional },
new { controller = new NotEqualConstraint("Admin")},
new string[] {"DailyDealsHQ.WebUI.Controllers"}
);
and here's the implementation of the NotEqualConstraint:
public class NotEqualConstraint : IRouteConstraint
{
private string match = String.Empty;
public NotEqualConstraint(string match)
{
this.match = match;
}
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return String.Compare(values[parameterName].ToString(), match, true) != 0;
}
}
There's probably other ways to solve this problem, but this fixed it for me :)

Categories

Resources