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.
Related
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.
Here is my controller
public class SpecializationsController : Controller
{
public ActionResult Action1()
{
//body
}
public ActionResult Action2()
{
//body
}
Default url for Action1 is of course /Specialization/Action1. I want to add prefix to all Actions in my controller to make my ulr like /prefix/Specialization/Action1.
I tried to add [RoutePrefix("prefix")] to my controller but it doesn't work. I would like to avoid adding [Route] attribute for each action in my controller. So how can I add this prefix?
I would create Areas:
Areas are an ASP.NET MVC feature used to organize related functionality into a group as a separate namespace (for routing) and folder structure (for views). Using areas creates a hierarchy for the purpose of routing by adding another route parameter
I know you may think this is an overkill for simply having a "prefix" but the reason I suggest this approach is because if you have the need to add a "prefix", chances are you have the need to separate the views, models etc. as well.
You need to add a route to your route collections instead of using route attributes
routes.MapRoute(
"Route",
"prefix/{controller}/{action}",
new { controller = "Specializations", action = "Index" });
I have an existing API I'm moving over to WebAPI, so I'm not free to change the URL. Breaking existing clients is not an option for me.
Knowing that, the original API would accept either a Guid (an ID) or a string (a name) for a given action method. The old API handler would decipher the URL parameter and send the request to a controller action designed to accept the given parameter type.
As an example:
Get(Guid id)
versus
Get(string name)
With WebAPI, the parameter binding is greedy across value types, so depending on which is first in the controller source file, that action is the one invoked. For my needs, that's not working. I was hoping the binder would realize the conversion to a Guid would fail for a name and then select the more generic string-based action. No dice. The Guid simply comes across as a null value (interestingly since it's a value type, but that's what I'm getting in the debugger at a certain point in the processing).
So my question is how best to handle this? Do I need to go as far as implementing a custom IHttpActionSelector? I tried the attribute routing approach (with constraints), but that isn't working quite right (bummer as it looks cool). Is there a mechanism in WebAPI that accounts for this I don't (yet) know about? (I know I can hack it in by testing the string for Guid-ness and invoking the other controller method, but I'm hoping for a more elegant, WebAPI-based solution...)
I'd spent a lot of time trying to fit attribute-based routing in, but I've not got that to work. However, I did solve my particular issue using route constraints. If you register the more-constrained route first, WebAPI (like MVC) will apply the constraints and skip over more-constrained routes until it finds one that it can select, if any.
So, using my example, I'd set up routes like so:
_config.Routes.MapHttpRoute(name: "ById",
routeTemplate: "product/{id}",
defaults: new { controller = "ProductDetails" },
constraints: new { id = #"^\{?[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\}?$" });
_config.Routes.MapHttpRoute(name: "ByName",
routeTemplate: "product/{name}",
defaults: new { controller = "ProductDetails" });
The first route accepts a constraint in the form of a regular expression for a Guid. The second accepts all other values, and the controller will deal with non-product names (returns a 404). I tested this in the self-hosted WebAPI server and it works fantastically.
I am sure attribute-based routing would be more elegant, but until I get that to work, it's routing the old way for me. At least I found a reasonable WebAPI-based solution.
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);
For the project that I am working on, we have companies. Companies have contacts and facilities. Based on the business rules, the flow is you select a company to access the contacts or facilities.
EDIT:
Entities are companies, facilities and contacts.
As each entity has it's own workflows, so they all have their one AREA in the code. What would be a clean way to make sure tha the routing url would be something like below:
/Company/1234/contact/456
/Company/1234/facility/679
If there was a way to next areas that would seem like a good way, but could make the code messy.
I don't think you need to use areas
To do what you want could be done by defining routes in the global.asax for each "subcontroller" to help the engine. (I have assumed that Contact and Facility are separate controllers?)
It does mean being very specific about what pattern goes to what route, but I think the below will do what you need.
Add these 2 new routes in the global asax (above the default route):
routes.MapRoute(
"ContactRoute", // Route name
"Company/{id}/Contact/{action}/{contactId}", // URL with parameters
new { controller = "Contact", action = "Index"
} // Parameter defaults
);
routes.MapRoute(
"FacilityRoute", // Route name
"Company/{id}/Facility/{action}/{facilityId}", // URL with parameters
new { controller = "Facility", action = "Index"
} // Parameter defaults
);
I'm not 100% on the code, you might need to define the contactId and facilityId as optional, but I hope it gives you an idea?