Removing area name from route in MVC - asp.net-mvc

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

Related

Get current action and controller and use it as a variable in an Html.ActionLink?

I need to be able to dynamically retrieve the current action and controller name of whatever page you're on, and actually use them to create a new HTML.ActionLink that links to the same action and controller name, but in a different area. So I guess I need to retrieve the current action and controller name as variables to use in building a new HTML.ActionLink.
So, if I'm on the www.site.com/about page, I need to have a link dynamically generated to the www.site.com/es/about page.
I've basically built a spanish translated version of my site in a separate area folder (with the same named controllers and actions and views, just content is all in spanish). I need the user to be able to toggle back and forth between the english version of the page (which is the default, and resides in the root site views) and the spanish version (whose views resides in the area folder "es") of whichever page they're currently on. I can't "hardcode" these links because I need this in a shared partial view which is the _topNavigation in my _Layout used on every page.
Please let me know if I need to clarify. I'm sure using "areas" wasn't really the way to go when localizing an application, but I'm still trying hard to teach myself asp.net MVC. I read many MANY tutorials and examples on localization, and I could just not get them to work or make sense.
I should also add that I already know how to use HTML.ActionLink to go back and forth between the areas. I've managed to create the correct HTML.ActionLinks to any of the views in the spanish (es) area, and to any of the views in the default site. So that is not my question.
Any help is greatly appreciated! Thanks!
Use ViewContext.RouteData to determine current Controller and Action:
#Html.ActionLink("Spanish Version",
ViewContext.RouteData.Values["action"] as String,
ViewContext.RouteData.Values["controller"] as String,
new { Area = "es" })
Try this:
Html.ActionLink("Espanol", "action", "ControllerName", new { Area = "es" }, null)
If the actionName is only for #Html.ActionLink(), you can simply pass "null" as the actionName. The current actionName will be used.

MVC: ActionLink Remembers ID Field?

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.

ActionLink/RouteLink based on URL not Controller

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.

The view 'Index' or its master was not found.

The view 'Index' or its master was not found. The following locations were searched:
~/Views/ControllerName/Index.aspx
~/Views/ControllerName/Index.ascx
~/Views/Shared/Index.aspx
~/Views/Shared/Index.ascx
I got this error when using ASP.Net mvc area. The area controller action are invoked, but it seems to look for the view in the 'base' project views instead of in the area views folder.
What you need to do is set a token to your area name:
for instance:
context.MapRoute(
"SomeArea_default",
"SomeArea/{controller}/{action}/{id}",
new { controller = "SomeController", action = "Index", id = UrlParameter.Optional }
).DataTokens.Add("area", "YOURAREANAME");
This error was raised because your Controller method name is not same as the View's name.
If you right click on your controller method and select Go To View (Ctrl+M,Ctrl+G), it will either open a View (success) or complain that it couldn't find one (what you're seeing).
Corresponding Controllers and View folders name have the same names.
Corresponding Controller methods & Views pages should same have the same names.
If your method name is different than view name, return view("viewName") in the method.
Global.asax file contain the URL Route.
Default URL route like this.
"{controller}/{action}/{id}"
So,Try this.
1. Right click your controller method as below.
Example: let say we call Index() method.Right click on it.
2. Click Add View.. and give appropriate name.In this example name should be Index.
Then it will add correct View by creating with relevant folder structure.
Check the generated code at MyAreaAreaRegistration.cs and make sure that the controller parameter is set to your default controller, otherwise the controller will be called bot for some reason ASP.NET MVC won't search for the views at the area folder
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"SomeArea_default",
"SomeArea/{controller}/{action}/{id}",
new { controller = "SomeController", action = "Index", id = UrlParameter.Optional }
);
}
Where this error only occurs when deployed to a web server then the issue could be because the views are not being deployed correctly.
An example of how this can happen is if the build action for the views is set to None rather than Content.
A way to check that the views are deployed correctly is to navigate to the physical path for the site on the web server and confirm that the views are present.
The problem was that I used MvcRoute.MappUrl from MvcContrib to route the context.Routes.
It seems that MvcContrib routing mapper was uncomfortable with area routing.
You most likely did not create your own view engine.
The default view engine looks for the views in ~/Views/[Controller]/ and ~/Views/Shared/.
You need to create your own view engine to make sure the views are searched in area views folder.
Take a look this post by Phil Haack.
I had this problem today with a simple out of the box VS 2013 MVC 5 project deployed manually to my local instance of IIS on Windows 8. It turned out that the App Pool being used did not have the proper access to the application (folders, etc.). After resetting my App Pool identity, it worked fine.
right click in index() method from your controller
then click on goto view
if this action open index.cshtml?
Your problem is the IIS pool is not have permission to access the physical path of the view.
you can test it by giving permission. for example :- go to c:\inetpub\wwwroot\yourweb then right click on yourweb folder -> property ->security and add group name everyone and allow full control to your site . hope this fix your problem.
It´s still a problem on the Final release.. .when you create the Area from context menu/Add/Area, visual studio dont put the Controller inside de last argument of the MapRoute method. You need to take care of it, and in my case, I have to put it manually every time I create a new Area.
You can get this error even with all the correct MapRoutes in your area registration. Try adding this line to your controller action:
If Not ControllerContext.RouteData.DataTokens.ContainsKey("area") Then
ControllerContext.RouteData.DataTokens.Add("area", "MyAreaName")
End If
If You can get this error even with all the correct MapRoutes in your area registration and all other basic configurations are fine.
This is the situation:
I have used below mentioned code from Jquery file to post back data and then load a view from controller action method.
$.post("/Customers/ReturnRetailOnlySales", {petKey: '<%: Model.PetKey %>'});
Above jQuery code I didn't mentioned success callback function.
What was happened there is after finishing a post back scenario on action method, without routing to my expected view it came back to Jquery side and gave view not found error as above.
Then I gave a solution like below and its working without any problem.
$.post("/Customers/ReturnRetailOnlySales", {petKey: '<%: Model.PetKey %>'},
function (data) {
var url = Sys.Url.route('PetDetail', { action: "ReturnRetailOnlySalesItems", controller: "Customers",petKey: '<%: Model.PetKey %>'});
window.location = url;});
Note: I sent my request inside the success callback function to my expected views action method.Then view engine found a relevant area's view file and load correctly.
I have had this problem too; I noticed that I missed to include the view page inside the folder that's name is same with the controller.
Controller: adminController
View->Admin->view1.cshtml
(It was View->view1.cshtml)(there was no folder: Admin)
This error can also surface if your MSI installer failed to actually deploy the file.
In my case this happened because I converted the .aspx files to .cshtml files and visual studio thought these were brand new files and set the build action to none instead of content.
I got the same problem in here, and guess what.... looking at the csproj's xml' structure, I noticed the Content node (inside ItemGroup node) was as "none"... not sure why but that was the reason I was getting the same error, just edited that to "Content" as the others, and it's working.
Hope that helps
Add the following code in the Application_Start() method inside your project:
ViewEngines.Engines.Add(new RazorViewEngine());
I added viewlocationformat to RazorViewEngine and worked for me.
ViewLocationFormats = new[] {
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Areas/Admin/Views/{1}/{0}.cshtml",
"~/Areas/Admin/Views/Shared/{0}.cshtml"
};

How Can I Stop ASP.Net MVC Html.ActionLink From Using Existing Route Values?

The website I'm working on has some fairly complicated routing structures and we're experiencing some difficulties working with the routing engine to build URLs the way we need them to be built.
We have a search results page that uses RegEx based pattern matching to group several variables into a single route segment (i.e. "www.host.com/{structuralParameters}" can be the following: "www.host.com/variableA-variableB-variableC" - where variables A through C are all optional). This is working for us fine after a bit of work.
The problem we are experiencing resolves around an annoying feature of the ActionLink method: if you point to the same controller/action it will retain the existing route values whether you want them or not. We prefer to have control over what our links look like and, in some cases, cannot have the existing parameters retained. An example would be where our site's main navigation leads to a search results page with no parameters set - a default search page, if you like. I say this is an annoying feature because it is a rare instance of the ASP.Net MVC Framework seemingly dictating implementation without an obvious extension point - we would prefer not to create custom ActionLink code to write a simple navigation link in our master page!
I've seen some say that you need to explicitly set such parameters to be empty strings but when we try this it just changes the parameters from route values into query string parameters. It doesn't seem right to me that we should be required to explicitly exclude values we aren't explicitly passing as parameters to the ActionLink method but if this is our only option we will use it. However at present if it is displaying in the query string then it is as useless to us as putting the parameters directly into the route.
I'm aware that our routing structure exasperates this problem - we probably wouldn't have any issue if we used a simpler approach (i.e. www.host.com/variableA/variableB/variableC) but our URL structure is not negotiable - it was designed to meet very specific needs relating to usability, SEO, and link/content sharing.
How can we use Html.ActionLink to generate links to pages without falling back on the current route data (or, if possible, needing to explicitly excluding route segments) even if those links lead to the same action methods?
If we do need to explicitly exclude route segments, how can we prevent the method from rendering the routes as query string parameters?
This seemingly small problem is causing us a surprising amount of grief and I will be thankful for any help in resolving it.
EDIT: As requested by LukLed, here's a sample ActionLink call:
// I've made it generic, but this should call the Search action of the
// ItemController, the text and title attribute should say "Link Text" but there
// should be no parameters - or maybe just the defaults, depending on the route.
//
// Assume that this can be called from *any* page but should not be influenced by
// the current route - some routes will be called from other sections with the same
// structure/parameters.
Html.ActionLink(
"Link Text",
"Search",
"Item",
new { },
new { title = "Link Text" }
);
Setting route values to be null or empty string when calling Html.ActionLink or Html.RouteLink (or any URL generation method) will clear out the "ambient" route values.
For example, with the standard MVC controller/action/id route suppose you're on "Home/Index/123". If you call Html.RouteLink(new { id = 456 }) then MVC will notice the "ambient" route values of controller="Home" and action="Index". It will also notice the ambient route value of id="123" but that will get overwritten by the explicit "456". This will cause the generated URL to be "Home/Index/456".
The ordering of the parameters matters as well. For example, say you called Html.RouteLink(new { action = "About" }). The "About" action would overwrite the current "Index" action, and the "id" parameter would get cleared out entirely! But why, you ask? Because once you invalidate a parameter segment then all parameter segments after it will get invalidated. In this case, "action" was invalidated by a new explicit value so the "id", which comes after it, and has no explicit value, also gets invalidated. Thus, the generated URL would be just "Home/About" (without an ID).
In this same scenario if you called Html.RouteLink(new { action = "" }) then the generated URL would be just "Home" because you invalidated the "action" with an empty string, and then that caused the "id" to be invalidated as well because it came after the invalidated "action".
Solution at the root of the problem
It seems that the optimal solution (that doesn't smell like a workaround) is the one that solves the problem where it has roots and that's in routing.
I've written a custom Route class called RouteWithExclusions that is able to define route value names that should be excluded/removed when generating URLs. The problem is when routing falls through routes table and subsequent routes don't have the same route value names...
The whole problem is detailed and explained in my blog post and all the code is provided there as well. Check it out, it may help you solve this routing problem. I've also written two additional MapRoute extension methods that take an additional parameter.
If you want total control of the link, just build the link yourself:
Click Here
Substitute whatever you need inside the href attribute.

Resources