I have a page in my MVC site that is used to browse through a folder structure on the server, but the wild card mapping is not working for documents.
The mapping works for folder names such as
http://localhost:4321/Document/Folder/General_HR
Which maps to a shared drive folder like T:\root\General_HR in the controller.
But I get a 404 and the controller method isn't hit when trying to access a file such as
http://localhost:4321/Document/Folder/General_HR/Fire_Drill_Procedures.doc
Here is the route mapping
routes.MapRoute("Document Folder",
"Document/Folder/{*folderPath}",
new { controller = "Document", action = "Folder" });
I also have the standard MVC route that is applied after the one above:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I tried commenting out all other routes, but it still doesn't work.
I had had it working at one point, but I don't know what has changed since then. It seems that it's looking for a resource located at C:\MyProject\General_HR instead of being routed to the controller. How can I fix my mapping to account for this? According to this answer it should be working.
My controller method works like this: if the URL parameters contains an extension, then I return File(filePath), otherwise I create a view model and return View(viewModel). So when I click on something with an extension, it should still point to this same controller method and return the file.
public virtual ActionResult Folder(string folderPath)
{
var actualFolderPath = folderPath;
if (string.IsNullOrEmpty(actualFolderPath))
{
actualFolderPath = DocumentPathHelper.RootFolder;
}
else
{
actualFolderPath = DocumentPathHelper.GetActualFileLocation(actualFolderPath);
}
if (System.IO.File.Exists(actualFolderPath))
{
return File(actualFolderPath, MimeMapping.GetMimeMapping(actualFolderPath));
}
var vm = new FolderViewModel();
//build vm
return View(vm);
}
I "solved" this by altering my urls to the documents by replacing the . with a placeholder, then replacing it back in the controller.
for each item:
folderPath = folderPath.Replace(".", "___");
Then in the controller:
public virtual ActionResult Folder(string folderPath)
{
var actualFolderPath = folderPath.Replace("___", ".");
Related
I guess I don't completely understand how urls work with C# projects, in the sense that I don't know how to specify a url to go through the controller and not just return a aspx page.
Say I am trying to get to my project's Index page through a Controller named "ScholarshipController.cs". I would think to hit the Index method/action in this controller, my url would be as follows (my app's name is "TuitionAssistance" fyi):
http://localhost/TuitionAssistance/Scholarship/Index
However, running this url just returns the aspx page named "Index.aspx" located in the "Scholarship" view file without hitting the Controller. Why is this happening, and how do I get it to go through the controller so the Index page, when loaded, will have the appropriate information loaded onto it?
Sorry if this is a dumb question. Any insight would be appreciated. Thanks!
Route.config:
using System.Web.Mvc;
using System.Web.Routing;
namespace ScholarshipTuitionAssistance
{
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
/* Scholarship */
/* Scholarship */
//routes.MapRoute("TuitionAssistance",
// "tuition/{name}",
// new { controller = "TuitionAssistance", action = "Index", name = "" });
routes.MapRoute(
name: "TuitionAssistance",
url: "{controller}/{action}/{employee_number}",
defaults: new { controller = "Home", action = "TuitionAssistance", employee_number = UrlParameter.Optional }
);
routes.MapRoute(
name: "Scholarship",
url: "{controller}/{action}/{employee_number}",
defaults: new { controller = "Home", action = "Scholarship", employee_number = UrlParameter.Optional }
);
routes.MapRoute(
name: "Details",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Scholarship", action = "Details", id = UrlParameter.Optional }
);
}
}
}
Your route (URL) cannot match anything that actually exists on the filesystem. In your example here, you apparently have a file, [document root]\Scholarship\Index.aspx. As a result, a request for Scholarship/Index will return that file, instead of invoking the ASP.NET MVC machinery to load a controller action.
In MVC ASP.NET, think of those types of links as a way to call your methods in your controller. When that link is accessed, your controller does a bunch of junk and then returns an ActionResult (or other things). This ActionResult, for the sake of this explanation, is the markup that is written in the corresponding view file. Controller - >index() will return the view called index under views - > controller. If you want to pass information to your view, you will pass a model that has all of your information in it to the view from your index controller (return View(MyFancyModel)). The view will have a razor line at the top such as: #model The.Namespace.Wherever.my.model.is
The scaffolded controllers and views in Visual Studio for the index page specifically, only pass a list of the items in the corresponding database.
I've recently added Areas to an existing MVC 4 web app. One of my areas has a Home controller, so obviously when I navigate to /MyArea/Home/Index I want to display it's Index view. Initially I was getting the following error:
Multiple types were found that match the controller named 'Home'. This can happen if the route that services this request ('{controller}/{action}/{id}') does not specify namespaces to search for a controller that matches the request. If this is the case, register this route by calling an overload of the 'MapRoute' method that takes a 'namespaces' parameter.
The request for 'Home' has found the following matching controllers:
MyApp.Controllers.HomeController
MyApp.Areas.MyArea.Controllers.HomeController
Researching the issue I found that I needed to add default namespaces to my calls to MapRoutes() and that's stopped the error. Unfortunately I now find that when I go to /MyArea/Home/Index the app actually displays the view for /Home/Index instead - I can't display actions from the Area's Home controller.
Here's my code:
Global.aspx.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
MyApp.RouteConfig
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},
namespaces: new[] {typeof(MyApp.Controllers.HomeController).Namespace}
);
}
MyApp.Areas.MyArea.MyAreaRegistration
public override void RegisterArea(AreaRegistrationContext context)
{
var ns = typeof(MyApp.Areas.MyArea.Controllers.HomeController).Namespace;
var routeName = AreaName + "_Route";
var routeUrl = AreaName + "/{controller}/{action}/{id}";
var routeDefaults = new {controller = "Home", action = "Index", id = UrlParameter.Optional};
var routeNamespaces = new[] {ns};
context.MapRoute(
name: routeName,
url: routeUrl,
defaults: routeDefaults,
namespaces: routeNamespaces
);
}
Anyone got any bright ideas on how to solve this?
Update:
The problem only occurs when calling an action that exists in both HomeController classes, like Index. Calling an action in the Area's HomeController that does NOT exist in the default HomeController displays the correct view.
Update 2:
Turns out that this was a classic PICNIC error - a simple typo in my code, so the action went looking for a view that did not exist. As such, it went for the first matching view it could find - the one in the "root"
You need to add new { Area = "MyArea" } in the routeValues parameter when you are linking to the one in the MyArea area.
In your RegisterArea function you should update the routeDefaults to also include the area name.
var routeDefaults = new {controller = "Home", action = "Index", area = AreaName, id = UrlParameter.Optional};
<facepalm>
In the process of importing the code for the Area I made a typo in the view's folder name, so MVC wasn't able to find the correct index.cshtml for the area action.
</facepalm>
By the looks of things, when MVC can't find the area action's view it uses the default view instead. Once I'd fixed the name of the folder in Areas\MyArea\Views to match the controller name everything worked as expected.
+1 to both of the folks who answered - your responses were helpful and I've employed both in my updated code
We have several controllers we would like grouped in a subfolder (Admin) on our site. We would have the main pages be at the root level. But for these pages we would like to have our site path be something like this:
www.domain.com/Admin/{controller}/{action}/{id}
I've set the RouteConfig.cs file like this:
routes.MapRoute
(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Submission", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute
(
name: "Admin",
url: "Admin/{controller}/{action}/{id}",
defaults: new { controller = "SystemSecurity", action = "Index", id = UrlParameter.Optional }
);
I've set up one of the controllers like this:
[RoutePrefix("Admin/SystemSecurity")]
public class SystemSecurityController : Controller
{
private MkpContext _db = new MkpContext();
// GET: SystemSecurity
public ActionResult Index()
{
var roles = _db.Role.Select(r => r);
return View(roles.ToList());
}
}
In our solution the path to the controller is: \Controllers\Admin\SystemSecurityController.cs
The path to the view is: \Views\Admin\SystemSecurity\Index.cshtml
But we get the 'Resource cannot be found' error message.
I've also tried it with no RoutePrefix, and also with RoutePrefix("Admin").
If I put the view here: \Views\SystemSecurity\Index.cshtml
and navigate with this path: www.domain.com/SystemSecurity/Index
the page loads, so I know the controller and page are working.
What am I doing wrong?
I found out about MVC Areas. (http://www.philliphaydon.com/2011/07/mvc-areas-routes-order-of-routes-matter/)
By adding an Area to my project (Right click on Project's name, then Add - Area) I am able to better group my code.
Many of the pages I found either don't mention Areas or when the do, it's in passing, but they don't explain them. Hopefully this will help somebody else.
I was looking for a way to load different data from the database based on an ID or name in the URL.
For example:
This is the link to the view:
http://localhost:50830/CreateSale/Order
Now I want something like this:
http://localhost:50830/CreateSale/Order/5
OR
http://localhost:50830/CreateSale/Order/Cookies
And then based on the ID (5) or the name (Cookies), the controller CreateSale will load the data that is linked to the ID/name in the Order view. This data will be different for every ID/name.
EDIT
So basically what I want is a way to create a URL like the one I showed. So when I type the link into the browser, depending on the the last part (5 or Cookie), it should show the desired data on the page.
For example:
If the link is:
http://localhost:50830/CreateSale/Order/Cookies
Then I want to see data about ordering cookies on the page.
When the link is:
http://localhost:50830/CreateSale/Order/Toys
Then I want to see data about ordering toys on the page.
Is there any way to do this in asp.net mvc4?
Thanks!
This should work out of the box with the default routing. On the controller method read the Id and pass it to your method that reads from the database, then pass the results back to the view.
public ActionResult Order(string id)
{
var data = DbLoader.Execute(id);
return View(data);
}
On your view, read the data you passed to it. e,g in Index.cshtml.
#foreach (var dataItem in Model)
{
//Display something
}
This assumes you do not have a custom route for "CreateSale/Orders" and is using the default route of :
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
If you do have a custom route for "/CreateSale/Order" ensure the url ends with "/{id}" like the default route.
EDIT
To generate urls use the Url.Action method either in your view or controller. E.g
#Url.Action("Order", "CreateSale", new RouteValueDictionary { { "id","5" } }, Request.Url.Scheme, Request.Url.Host) //generates http://localhost:50830/CreateSale/Order/5
#Url.Action("Order", "CreateSale", new RouteValueDictionary { { "id","Cookies" } }, Request.Url.Scheme, Request.Url.Host) //generates http://localhost:50830/CreateSale/Order/Cookies
#Url.Action("Order", "CreateSale", new RouteValueDictionary { { "id","Toys" } }, Request.Url.Scheme, Request.Url.Host) //generates http://localhost/CreateSale/Order/Toys
I know I can change the routing in the RouteConfig in my MVC application:
routes.MapRoute(name: "epage", url: "view/SpecificURL", defaults: new {
controller = "ePage",
action = "Index"
})
but I am wondering how to redirect for values comming from a db. There is one row in my db that has titles . So for each title comming from the db I want to redirect to a specific url
ex.
the title in db may be "pink" I want www.mydomain.com/pink to be rerouted to a specific url . The URL that I want it redirected to is also in the db. I looked at lots of questions on this and can not seem to find any that dynamically change the routing of urls
You can setup a route like this:
routes.MapRoute(name: "Default",
url: "{id}",
defaults: new { controller = "Home", action = "Index" });
Then in your controller (the HomeController in my case):
public ActionResult Index(string id)
{
ContentResult cr = new ContentResult();
// Do a DB lookup here to get the data you need from the database to generate the appropriate content.
cr.Content = id;
return cr;
}
This example simply returns the string that was sent. So now if I browse to http://localhost/mysite/pink I would get "pink" back as my result. You could easily use this method to then do a lookup to your custom database to determine the correct content to return.
If your existing routes don't let you take this route :), then you can always do a SQL query in the RegisterRoutes method and populate the route table from that.