Mapping ASP.NET MVC3 Controllers to views - c#

Is there any way of mapping multiple controllers to a single view directory? In my application I would like to have a 1 to 1 mapping between controllers and views. The reason for this is that each view will have a variety of ajax actions that the controller will provide methods for and having multiple views per controller will result in overly large controllers.
Presently I have something like this:
/Controllers
/Dir1Controller
...
/Views
/Dir1
/FirstView.cshtml
/SecondView.cshtml
...
So Dir1 Controller maps to the directory Dir1 and needs to provide all the actions for the views FirstView, SecondView, etc. Instead, I would prefer something like this:
/Controllers
/FirstController
/SecondController
...
Where both controllers map to Dir1 but return the appropriate view when an action is executed.
None of this seems to be a problem on the route mapping side of things but there doesn't seem to be a way for me to tell it how to find the correct view. FirstController will only look in /Views/First for views. Is there any way I can direct it to look in /Views/Dir1?
I looked at using areas but they seem to be directed at spiting up large sites. Conceptually all of these pages should be part of the same area. I just want to keep the size of my controllers bounded.

If your Ajax calls are related to a single controller then stick to the existing pattern and have methods in your controller. If they can be reused put them in partial views or controllers organized by their purpose and when doing your Ajax calls simply request the controller and action. This is the typical pattern for a reason, why step outside of it? :)

Why not just return the view explicitly?
return View("~/Controllers/FirstController/view.cshtml");
If all the actions in a controller return the same view, use a constant at the top of the Controller class, something like:
public class myController
{
private string ViewPath = "~/Controllers/FirstController/view.cshtml";
...
That way you only have to set the path to the view once in each controller. Then in each of your action methods:
return View(ViewPath);

Related

ASP.NET Core 2.0 URLs

I'm developing a large ASP.NET Core 2 web application but I still get confused with URLs.
When fist learning, I thought URL's took on the name of the View, but discovered they come from the Controller method.
Here is an example to convey my issue:
I have a controller method named Daysheet, which returns a view and model with the same name.
In the Daysheet view, I call various controller methods from Javascript to perform specific actions. One of them is called AssignStaff which takes two integer parameters.
In the AssignStaff method I again return the Daysheet view with model, but now my URL is "AssignStaff"!
I can't just do a redirect because the whole Daysheet model is not being passed to the AssignStaff method.
I have many situations like this where after calling an action, I end up with another URL that I don't want.
UPDATE/EDIT
Thanks for assistance and apologies if my explanation is confusing. I simply have a view called Daysheet that uses a model. I want to call various controller methods to perform various actions, but I want to stay on the "Daysheet" view/URL.
As mentioned, I can't just redirect because in the action method I no longer have the whole model from the Daysheet view. Also, if I redirect I can't pass the whole model because that causes an error saying the header is too long. I think my only choice may be to use ajax for the actions so that the URL doesn't change.
When you just do Return View("") name in a Controller Action, the URL will be the name of the Action you are using.
If you want to redirect to some specific Action, that will help to make sure the Url matches to where you are. You might want to read more about it here.
To do so, use:
RedirectToAction()
The URLs your application responds to are called "routes", and they are either created by convention or explicitly. The default is by convention, of course, which is a URL in the form of /{controller=Home}/{action=Index}. Index is the default action if that portion of the route is left off, so a request to /foo will by convention map to FooController.Index. HomeController is the default controller, so an empty path (e.g. http://sample.com) will by convention invoke HomeController.Index.
Razor Pages have their own conventions. These do somewhat follow the file system, but exclude the Pages part of the path. So a Razor Page like Pages/Foo/MyRazorPage.cshtml, will load up under /Foo/MyRazorPage.
There there is the Route attribute, which allows you to specify a totally custom route. This attribute can be applied to a controller class and individual actions in the class. For example:
[Route("foo")]
public class MyAwesomeController
{
[Route("bar")]
public IActionResult MyAwesomeAction()
{
return View();
}
}
With that, a request to /foo/bar will actually invoke MyAwesomeController.MyAwesomeAction.

Where is a good place to put common business logic that should run on every view?

I have a project in which I need to check for and add a cookie, regardless of which view the user is currently on. I can place the code inside of the _Layout partial view within a code block, but I have doubts that's the conventional place for it. Where should it go?
View is generally wrong place to put logic.
Action filter is one possible way to centralize the code and allow easy customization, especially for something that sound so close to behavior of AuthorizeAttribute filter.
See Action Filtering in ASP.Net MVC for information.
public class MyCookieFilter : ActionFilterAttribute ...
[MyCookieFilter]
public ActionResult Index()
{
// The action method logic.
}
Side note: when searching for documentation be carefull to distinguish MVC and WebAPI classes - many have similar names and similar behavior, but can cause some confusion when applied to wrong objects.

Correct use of controllers and views

Assuming a web page, where we want to display a list of movies, register movies and edit those movies.
You could create a controller, and within the controller treat the three views (Index, Create, Edit).
Or you could create two controllers, one for treating the Index and the other to address the Create and Edit. (Since Creating and Editing Model or ViewModel share)
Or you could create three controllers, one for each view.
It can be done in three ways.
But which one is right and why?
you'd want to create one controller for this, MoviesController, for example. The operations within the controller will then make sense since the URL will look like this...
Movies/Create
Movies/Edit
Movies //this will just list the movies.
The controller could then be passed a Movies repository for manipulating or listing the data.
Obviously, you can create three controllers, but this would be overkill and cause maintenance headache.
Because your business model is one , i think you have to use one controller and your required actions and views.
Controllers should be grouped logically around an object\functionality. If all of these three actions are related to Movies, then it should be MovieController with three actions\views.
Making new controller for each action will make your code a bit of mess.
You should create a controller with three actions and views (Index, Create, Edit).

MVC URL path extensions

I was just wondering whether its possible to have something like this: I have an Area named Admin and a Controller named 'Edit'. Within this controller I have my Index() which simply lists a bunch of hyperlinks that is treated by the 'Brand' action.
Therefore my url so far is: Admin/Edit/{Brand}.
My question is whether it is possible to have for example: Admin/Edit/{Brand}/Create (as well as edit and delete). This isn't to delete brands, its just to create things within those brands?
I approach that my approach may be misguided and this may necessitate being split into multiple controllers or whatever so don't think that I would like a workaround to make it work this way.
You could define the following route in your area registration:
context.MapRoute(
"Admin_default",
"Admin/{controller}/{brand}/{action}",
new { action = "Index" }
);
And if you wanted to have other controllers than Edit in this area which have the default route, you could register 2 routes but you will have to define a constraint for the {brand} token or the routing engine won't be able to disambiguate between a brand and a controller action name.

ASP.NET MVC3 - How to serve View() from another controller

So in order accomplish what I asked in this post I did the following:
[iPhone]
[ActionName("Index")]
public ActionResult IndexIPhone()
{
return new Test.Areas.Mobile.Controllers.HomeController().Index();
}
[ActionName("Index")]
public ActionResult Index()
{
return View();
}
Which still serves the same view as the Index action method in this controller. Even though I can see it executing the Test.Areas.Mobile.Controllers.HomeController().Index() action method just fine. What's going on here? And how do I serve the Index view from Mobile area without changing the request URL (as asked in the original post referenced above)?
You have a few options:
Redirect to the Action you'd like to return: return RedirectToAction("Action-I-Want").
Return the View by name: return View("The-View-I-Want").
Note that with the 2nd approach you'd have to put your view in the "Shared" folder for all controllers to be able to find it and return it. This can get messy if you end up putting all your views there.
As a side note: The reason your work doesn't find the view is because default view engine looks for the view in the folder that "belongs" to the current executing controller context, regardless of what code you're calling.
Edit:
It is possible to group all "mobile" views in the same folder. On your Global.asax (or where ever you're setting up your ViewEngine, just add the path to your mobile View in the AreaViewLocationFormats. Mind you, you'll still have to name your views differently.
You can also write your own view engine. I'd do something like detecting the browser and then serving the right file. You could setup a convention like View.aspx, and View.m.aspx.
Anyhow, just take a look at WebFormViewEngine and you'll figure out what works best for you.
The easiest way to send a request to a view handled by another controller is RedirectToAction("View-Name", "Controller-Name").
There are overloads of View() that take route information that might work as well, but they'd require more effort to set up.
Well actually the easiest way is to make one version of your site programmed on standards instead of browser detection :D -- however in direct response to accomplish what it in a more of a ASP.NET mvc fashion, using:
RedirectToAction("ViewName", "ControllerName");
is a good method however I have found it is more practical if you feel you must program for different browser standards to create a primary view and an alternate "mobile" view under your controllers views. Then instead of writing special code on every controller, instead extend the controller like so.
public class ControllerExtended : Controller
{
private bool IsMobile = false;
private void DetectMobileDevices(){ .... }
}
Then modify your controller classes to instead say ControllerExtended classes and just add the one line to the top of each Action that you have alternate views of like so:
public class ApplicationsController : ControllerExtended
{
// GET: /Applications/Index
public ActionResult Index() {
this.DetectMobileDevices();
if(this.IsMobile){
return RedirectToAction("MobileIndex");
} else {
// actual action code goes here
return View();
}
}
}
Alternately you can use return View("ViewName"); but from my experience you want to actually perform different actions as opposed to just showing the result in a different view as in the case of presenting an HTML table as opposed to a Flex table to help iPhone users since there is no flash support in the iPhone, etc. (as of this writing)

Categories