I have a controller that uses the following structure:
.com/Object/375
However, I can also use the following URL when I am accessing special admin rights
.com/Admin/Object/375
I use the same user controls whether you're in the Admin section or not, but they both point to the same Controller Object. I need for the links to maintain that URL structure and not try to kick an Admin user back to the Object controller. I am currently using the route name method, where these are my route names (in global.asax):
"Admin/-Object"
"Object/-Object"
"Object-Object"
These route names catch the following routes:
Admin/Object, Admin/Object/555, Object, Object/323
I then use the following in a route link
Html.RouteLink(id, Request.Url.Segments[1] + "-Object", new { id = id })
This works just fine, but has an odd smell - any other ideas?
To clarify: I need the URL to be properly created based on the current URL structure (with or without the Admin) and the routing will point to the correct controller (the same for both URLs) and the admin specific content will be injected into the page only if in the Admin section (based on URL).
Just to wrap this up, using ViewBag is probably a better idea because using the URL segment might result in unexpected errors, especialy if you move the controls or views around.
Related
Currently, we are working on HRIS (Human Resource Information System). We have different user types such as Admin, HR, Employee. But that user types are not static. We want to have different route for each user type.
e.g.
https://website/admin/{controller}/{id}
The route will depend on the user who logged-in in the system. Will read its user type.
May we know if there's a way to configure the route for each user type?
The solution from my point of view would be:
You create a route (URL actually) that matches the pattern:
https://website/admin/3
that holds all the logic for this user - probably, invoking Model, asking for the respective user controller, e.g., superadmin and later on redirecting to...
Another URL matching
https://website/admin/superadmin/3
that now has both controller = superadmin and variable id = 3. It is not said, that the route should be different - you can implement the logic when the controller is either a id (integer) or string to keep the logic more centralized.
Just to mention:
https://website/admin/3
should be fine - you can process the request from the respective Controller without redirecting (see 1. point)
This is very similar to the following question: MVC role-based routing
Essentially you use areas, combined with a routing constraint.
This is a follow-on to an earlier stackoverflow question (link text).
If you use the default routing definition, which ends with {id}, then if you have an ActionLink whose target is the same method as generated the page the ActionLink is on, the framework automagically includes the id in the callback url, even if you didn't request it.
For example, if you're displaying a page from the following URL:
http://www.somedomain.com/AController/SameMethod/456
and the page cshtml file has an ActionLink like the following:
#Html.ActionLink("some text", "SameMethod", ARouteValueDictionary, SomeHtmlAttributes)
then whether or not you have "id" included in ARouteValueDictionary, it will show up in the generated URL.
This only occurs if you call back to the same method that generated the page in the first place. If you call back to a different method on the same controller the {id} field does not get inserted into the generated URL.
I don't necessarily have a problem with this. But I am curious as to why the designers took this approach.
FYI, I discovered this feature because I'd inadvertently been depending on it in my website design. I have to pass the ID field back to the server, along with a bunch of other information...only I'd never explicitly added the ID information to the RouteValueDictionary. But because most of my callbacks were to the same action method that had generated the page in the first place I was having the information included anyway.
You can imagine my surprise when a new component -- which I was sure was "essentially identical" to what was already working -- failed. But because the new component had a different target action method, the magic went away.
Edit:
Modified the explanation to clarify that including the {id} field in the generated URL is contingent upon calling the same method as generated the page in the first place.
...the framework automagically includes the id in the callback url,
even if you didn't request it.
I would prefer the term "ambiently" over "automagically". You can think of route tokens already in the URL as "ambient" to your HtmlHelper and UrlHelpers.
But I am curious as to why the designers took this approach.
Consider a Controller that groups together, say 5 actions. Those 5 may have links to each other, but not a lot of links outside the group. The simplest overload of Html.Action takes only 2 args: the text to render, and the action name.
This makes shorthand for linking around from action to action within these views. Since they are all on the same controller, and that controller is already in the path for the current action, MVC reuses this value when you don't specify the controller name in the helper method. The same behavior extends to {id}, or any other route token you define.
I am using SimpleMvcCaptcha. My enviroment has different areas. I want to use this captcha in different forms on differet areas.
This module needs a controller with the name of say Captcha and one method in it.
If I create this controller in the same area that I am using it, then there is no problem, but if I use it in different areas, I am getting this error:
The controller for path '/AreaName/Captcha/GetImage/15fe4de1-fd46-4f2f-a0d1-74c397bd8365' could not be found.
How can I solve this problem? One way that I think I can solve this problem is to remove the area in route registration such as
routes.MapRoute(
"Default_Captcha",
"AreaName/Captcha/{action}/{id}",
new { area = string.Empty, controller = "Captcha", action = "GetImage", id = UrlParameter.Optional });
But I am still getting the same error.
Is there any way that I can remove area name from the link?
--- Update
The Captcha needs a controller for its operation (The controller with one method for getting captcha image).
I want to use it in a form which is implemented in MyArea. The form relates to a controller called MyController.
If I create a CaptchaController in this area with the required code inside it, the captcha works well (The Captcha image is shown).
Now assume that I have another area (MyArea2) and in this area, I have a controller called MyController2, then I need a new Captcha controller in this area too, otherwise, it doesn't show the image.
I cannot move CaptchaController to non area code section, as then image is not shown.
The URL to Captcha image is (for the Captcha used in view related to MyController):
http://mySite/MyArea/Captcha/GetImage/xxxxxxxxxxxx
The URL to Captcha image is (for the Captcha used in view related to MyController2):
http://mySite/MyArea2/Captcha/GetImage/xxxxxxxxxxxx
If I move the captcha controller to non area section, then none of the above url works and no image is shown.
If I can re route these URLs to the following URL, then they should work (I can have one Captcha controller in non area section) :
http://mySite/Captcha/getImage/xxxxxxxxxxxxxxxxxxx
How can I do this?
If your CaptchaController is not Area-specific, then it probably shouldn't be in an area. If you relocate and renamespace it to the main Controllers directory, it should become globally accessible.
If for some reason you don't want to do that, then you can register a route with higher precedence that maps directly to your action from a global location. The problem with the route you defined is that it does not specify the area of the controller as a parameter, but only seems to do so in the url itself. Its a little difficult for me to debug your routing though, because (1) it isn't clear whether you are adding or removing the sample route, (2) I can't see what routes are registered before or after the sample route, (3) the ambiguity of AreaName is confusing because we can't tell if it's the area where the mapping works or doesn't work
I have a simple create action to receive post form data, save to db and redirect to list view.
The problem is, after redirecttoaction result excutes, the url on my browser lost the action section. Which it should be "http://{hotsname}/Product/List" but comes out as "http://{hotsname}/Product/".
Below is my code:
[HttpPost]
public ActionResult Create(VEmployee model, FormCollection fc)
{
var facility = FacilityFactory.GetEmployeeFacility();
var avatar = Request.Files["Avatar"].InputStream;
var newModel = facility.Save(model, avatar);
return RedirectToAction("List");
}
The page can correctly render list view content, but since some links in this view page use relative url, the functions are interrupted. I am now using return Redirect("/Employee/List") to force the url. But I just wonder why the action name is missing. I use MVC3 and .Net framwork 4.
I am new to ASP.Net MVC, thanks for help.
Your route table definitely says that "List" action is default, so when you redirect to it as RedirectToAction("List") - routing ommits the action because it is default.
Now if you remove the default value from your routes - RedirectToAction will produce a correct (for your case) Url, but you'll have to double check elsewhere that you are not relying on List being a default action.
Well, Chris,
If you get the right content on http://{hotsname}/Product/ then it seems that routing make that URL point to List either indirectly (using pattern like {controller}/{action}) and something wrong happens when resolving URL from route or {action} parameter is just set wth default value List. Both URLs can point to the same action but the routing engine somehow takes the route without explicit action name.
You should check:
Order in which you define your routes
How many routes can possibly lead to EmployeeController.List()
Which one of those routes has the most priority
Default values for your routes
Just make the route with explicit values: employee/list to point to your List action and make sure that is the route to select when generating links (it should be most specific route if possible).
It would be nice if you provide your routes mappings here.
but since some links in this view
page use relative url, the functions
are interrupted.
Why do you make it that way? Why not generate all the links through routing engine?
When using the overload RedirectToAction("Action") you need to be specifying an action that is in the same controller. Since you are calling an action in a different controller, you need to specify the action with the alternate overload e.g. RedirectToAction("List", "Employee").
I want to build a ASP.NET MVC site so that the controller for a specific url is stored in the database instead of the URL.
The reason for that is that i'm building a CMS system and the users should be able to change the template (controller) without changing the URL. I also think that the name of the controller is not relevant for the end users and i want clean URL:s.
I realise that i could just add all routes at application start, but for a system with like 100 000 pages it feels like a bad idea.
Is it possible to store the url:s in the database and make a lookup for each request and then map that request to a specific controller?
Basically you'll have to implement your own IRouteHandler.
Part of the answer and some example code is in Option 3 of this question's answer:
ASP.NET MVC custom routing for search
More information:
http://weblogs.asp.net/fredriknormen/archive/2007/11/18/asp-net-mvc-framework-create-your-own-iroutehandler.aspx
Why couldn't you just do something like this:
-- Global.asax.cs --
routes.MapRoute(null, // Route name
"content/{id}", // URL with parameters
new { Controller = "Content", Action = "Show", Id = (string) null }); // Parameter defaults
-- /Controllers/ContentController.cs --
public class ContentController : Controller
{
public ActionResult Show(string id)
{
// Lookup the 'content' (article, page, blog post, etc) in the repository (database, xml file, etc)
ContentRepository repository = new ContentRepository();
Content content = repository.FindContent(id);
return View(content);
}
}
Such that a request to your site www.yoursite.com/content/welcome-to-my-first-blog-post would call ContentController.Show("welcome-to-my-first-blog-post").
I suppose ASP.NET can do many of the same things as PHP. If so there is a simple approach.
With rewrite rules you can easily send any traffic to any URL of the 100K to the same place. On that destination you could simply use the server variables containing the URL requested by the client and extract the location. Look it up in the DB and send the corresponding data for that URL back to the client on-the-fly.
"for a system with like 100,000 pages it feels like a bad idea."
It is a bad idea if you are creating a routing system that cannot be reused. The basic {controller}/{action}/{id} schema points you in the direction of reuse. This schema can be extended/revamped/recreated according to your needs.
Instead of thinking about how many pages you have think about how your resources can be grouped.
Instead of creating a heavy routing system why not create an anchor link control (ascx) which allows user to only add valid internal links. Keep a table in the db of your templates and their controllers to populate the control with it.