make routing be more generic, pagination - c#

I have enabled pagination and routing settings in global.asax like this
routes.MapRoute("Users", "Index/{page}",
new { controller = "Users", action = "Index", page = UrlParameter.Optional },
new[] { "MyProject.Controllers" });
Now I need to apply these to every controller which sends page parameter. How can I do this?
Thank you

There are two ways you can approach it.
Add a page parameter to all your Action methods:
public ActionResult SomeAction(int? page)`
{
if (page.HasValue) ...
}
Access the RouteData directly using:
RouteData.Values["page"]
I guess you might want to consider creating a Base Controller that handles repetitive tasks related to paging.

Related

Controller inheritance and choosing which controller has prevalence

I have a baseproject and different inheriting projects. The base project has controllers I may want to occasionally inherit and override (partially).
Base project:
public virtual ActionResult Index(string filter = "", int page = 1)
Sub project:
public override ActionResult Index(string filter = "", int page = 1)
Now I changed the routeConfig, so the routing is mapped to the logic from the correct namespace.
context.MapRoute(
"Routename",
"AreaName/{controller}/{action}/{id}",
new { controller = "ControllerName", action = "Index", id = UrlParameter.Optional },
new string[] { "ProjectName.Areas.AreaName.SpecificControllers"}
);
However, I want new added routes to be taken from the specific project should they exist there. The ones which are not existant should be taken from the base project's controller. (The specific controller basically starts out empty and will only contains methods for when overriding is desirable). To try and implement this functionality, I added the other project to the routing here:
context.MapRoute(
"Routename",
"AreaName/{controller}/{action}/{id}",
new { controller = "ControllerName", action = "Index", id = UrlParameter.Optional },
new string[] { "ProjectName.Areas.AreaName.SpecificControllers", "ProjectName.Areas.AreaName.GenericControllers"}
);
However, this obviously leads to the following error:
Multiple types were found that match the controller named 'MethodName'. This can happen if the route that services this request ('CRM/{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 'MethodName' has found the following matching controllers:
ProjectName.Areas.AreaName.SpecificControllers.ControllerName
ProjectName.Areas.AreaName.GenericControllers.ControllerName
Is there a way to implement this so that my routing will always look at the specific controller first and only at the generic controller if it cannot find the method in the specific controller?
Generally routing choose the base controller method as far as i know.
There is no direct support to resolve the issue you mentioned in this question.
There are couple of workarounds to resolve this.
Option 1 (My Favourite): Admin on base and Route on inherited controller.
To Use [Area] on the base controller and [Route] on the inherited controllers.
I personally like this approach because it keeps the code inside controller clean.
[Area("Admin")]
AdminBaseController: Controller { }
[Route("Users"))
UserAdminController : AdminBaseController { }
Url would be /Admin/Users/Action
Option 2: Use Specific Route Prefix in derived controller actions
[Route("Admin")]
AdminBaseController: Controller { }
public static string UserAdminControllerPrefix = "/Users";
UserAdminController : AdminBaseController {
[Route(UserAdminControllerPrefix + "/ActionName")]
public void ActionName() { }
}
Formed URL would be /Admin/Users/ActionName
you can choose whichever option which fits your style.
Hope this helps.
Both the approaches mentioned in this answer : ASP.NET Core MVC Attribute Routing Inheritance

How to set up routing so that Index does show?

So I know google can penalize a site if you have the same content on multiple urls... unfortunately, in MVC this is too common i can have example.com/, example.com/Home/ and example.com/Home/Index and all three urls would take me to the same page... so how do I make sure that whenever Index is in the url, that it redirects to the same without the Index and of course the same thing with the Home
Perhaps this little library may be useful for you.
This library is not very convinient in your case, but it should work.
var route = routes.MapRoute(name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
routes.Redirect(r => r.MapRoute("home_index", "/home/index")).To(route);
routes.Redirect(r => r.MapRoute("home", "/home")).To(route);
The way I handle this is for default pages like Index is to simply create an explicit route for only one of them. I.e. "example.com/People" would be the route for People/Index, and there would be no valid page at the url "/example.com/People/Index".
The Home example is unique in that it has potentially three different URLs. Again in this case I'd simply create a route for "example.com" for that Index action, and not support the other two urls. In other words, you would never link to the other forms of the URL, so their absence should never cause a problem.
We use a Nuget package called AttributeRouting to support this. When you specifiy a GET route for a page, it overrides the defaults for MVC.
Using AttributeRouting usually you'd map the index to [GET("")] but for the special case of Home where you also want to also support the root URL that omits the controller name , I think you'd also add an additional attribute with IsAbsoluteUrl:
public class HomeController : BaseController
{
[GET("")]
[GET("", IsAbsoluteUrl = true)]
public ActionResult Index()
{...
So I found a way to do it without any external Library...
In my RouteConfig I had to add these two routes at the top, just below the IgnoreRoute
routes.MapRoute(
"Root",
"Home/",
new { controller = "Redirect", action = "Home" }
);
routes.MapRoute(
"Index",
"{action}/Index",
new { controller = "Redirect", action = "Home" }
);
Then I had to create a new Controller called Redirect and I created a method for each of my other Controllers like this:
public class RedirectController : Controller
{
public ActionResult Home()
{
return RedirectPermanent("~/");
}
public ActionResult News()
{
return RedirectPermanent("~/News/");
}
public ActionResult ContactUs()
{
return RedirectPermanent("~/ContactUs/");
}
// A method for each of my Controllers
}
That's it, now my site looks legit. No more Home, no more Index in my URLs, this of course has the limitation of not being able to accept parameters to any of the Index methods of your Controllers though if it was really necessary, you should be able to tweak this to achieve what you want.
Just an FYI, if you want to pass an argument to your Index Action, then you can add a third route like this:
routes.MapRoute(
name: "ContactUs",
url: "ContactUs/{id}/{action}",
defaults: new { controller = "ContactUs", action = "Index", id = UrlParameter.Optional }
);
This will create a URL like this: /ContactUs/14

How to name/divide controllers in MVC3 application

At the moment I have 3 controllers: Home, Summary and Detail
However, each has only one action: Index, Display and Display respectively.
This smell bad to me.
I was hoping to use the MapRoute to allow:
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
instead of
myapp/Home
myapp/Summary/Display/prop1/prop2
myapp/Detail/Display/prop1/prop2/prop3
and thereby miss out the "Display" part...but again, this doesn't smell right. Although it works, it means manually adding links instead of using Html.ActionLink(...)
Would it be better to have Home/Index, Home/Summary and Home/Detail all in one controller?
I was hoping to provide a simple URL structure so users who know what they are doing could simply type it in as above...the "Home" part seems wasted?
I agree with #Tim that you should use a single controller. A controller is a logical grouping of actions; for example the CRUD operations for Foo. NerdDinner is a good example of this.
I disagree with the routes. You can do whatever you want with the routing; but it should be meaningful to the user. You probably just have a single catchall route similar to the one below.
routes.MapRoute("Default", //RouteName
"{controller}/{action}/{id}", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index",
id = UrlParameter.Optional}
)
You can have the routes you want by using a single controller.
Your desired urls:
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
The controller setup:
public class HomeController : Controller
{
public ActionResult Index() { ... }
public ActionResult Summary() { ... }
public ActionResult Details() { ... }
}
The routing setup:
routes.MapRoute("Home-Index", //RouteName
"myapp/Home", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index"});
routes.MapRoute("Home-Summary", //RouteName
"myapp/Summary/prop1/prop2", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Summary",
prop1 = UrlParameter.Optional,
prop2 = UrlParameter.Optional});
routes.MapRoute("Default", //RouteName
"{controller}/{action}/{id}", //RouteUrl
new { //RouteDefaults
controller = "Home",
action = "Index",
id = UrlParameter.Optional}
)
Now there are a few important things to note:
Routing works like a switch statement, the first url that matches is
the one it will use, that's why you have the catchall as the last
one.
The url defined in your map route can be whatever you want. It
doesn't have to match with the controller, because once you remove
the placeholders ({controller}, etc) it uses the default for
navigation. So the Home-Index route url could be myapp/foo/bar/baz
and id would still take you to the Home index action.
The placeholders work automagically. I have not been able to find a good resource explaining how the default route works.
Hope this helps.
Not sure if I totally get your question, but what about creating a base controller class that inherits from Controller, and have your shared actions there instead? That way you don't need to repeat yourself as much.
You need only one controller and inside it multiple actions..
public class HomeController : Controller
{
public ActionResult Index()
{
}
public ActionResult Summary()
{
}
public ActionResult Details()
{
}
}
In the action link
#Html.ActionLink("Details", "Details","Home");
that's enough, no need to add multiple controller..
Hope these helps..
You can pretty much go down any route you want when it comes to this, all just depends on what you want to achieve.
You can stick all the actions in the Home controller so your routes would be
myapp/Home
myapp/Home/Summary/prop1/prop2
myapp/Home/Details/prop1/prop2/prop3
In this option you have 1 controller, 3 actions, with 2 additional routes to handle the URls
It depends on what the summary and details are of though? Like if it is the summary of an order, i would prefer
myapp/Orders/Summary/prop1/prop2
myapp/Orders/Details/prop1/prop2/prop3
In this you would have your Home controller and the Index action, then an Orders controller with two actions. I would say that Summary and Details would generally suggest that you are displaying something anyway, so you would not need the "Display" part as you have in your suggestions.
If you want your URLS to be
myapp/Home
myapp/Summary/prop1/prop2
myapp/Detail/prop1/prop2/prop3
Then you make 3 controllers
HomeController
SummaryController
DetailController
Each of these will have 1 Action
public ActionResult Index() {
}
For the SUmmary and Detail controller you will just pass in some extra paramaters to the Index action

How to define route & controller structure for 2 controllers?

I want to create an MVC app to generate factsheets, but I'm not sure how to structure the routing and controllers.
It consists of an index page, which acts as a template for the layout of a number of independent panels, each of which contains different types of data.
I want to have a the route template like the following:
/Factsheets/Panels/PanelType?fundId=1&countryId=ABC
so these would be the URLs I'm using:
/Factsheets/Panels/NameAndDatePanel?Afund=1&county=IE
/Factsheets/Panels/AssetsPanel?fund=1&county=IE
I want a Factsheets controller to be able to supply the Panel controller with the configuration object it needs to generate the type of panel I request.
What should my routing structure look like?
What should my controller structure look like?
edit:
What changes if I want to have a roure structure as follows:
I want to have a the route template like the following:
/Factsheets/ContentArea/Panels/PanelType?fundId=1&countryId=ABC
so these would be the URLs I'm using:
/Factsheets/PageTop/Panels/NameAndDatePanel?Afund=1&county=IE
/Factsheets/PageTop/Panels/AssetsPanel?fund=1&county=IE
so that FactsheetsController is instantiated to contain the data needed by a ContentArea to know which data to supply to the panel it needs to generate.
What comes after the ? is the query string so it won't be reflected by the routing engine. So your route might look like this:
routes.MapRoute(
"Default",
"{controller}/{action}/{panelType}",
new { controller = "Factsheets", action = "Panels", panelType = UrlParameter.Optional }
);
And you would have the following controller:
public class FactsheetsController : Controller
{
public ActionResult Panels(string panelType)
{
return View();
}
}
UPDATE:
If the panelType parameter is not optional you could provide a regex constraint:
routes.MapRoute(
"Default",
"Factsheets/Panels/{panelType}",
new { controller = "Factsheets", action = "Panels", panelType = UrlParameter.Optional },
new { panelType = ".+" }
);

Access data from Asp.Net MVC URL

So if I have a URL such as mysite.com/user/1234/show which object can I use in the view that is displayed to get at the 1234 int in the URL above? For instance I want to have an action link that uses the user id again for the next controller action call such as mysite.com/user/1234/edit.
Thanks
You shouldn't have to go directly to the URL for the data. Instead, add a parameter to your route and action. Your route could look something like "{controller}/{id}/{action}" and your action something like:
ViewResult Show(int userId){
var viewData = new MyViewData(){
UserID = userId
};
return View(viewData);
}
Then, in your view, use Model.UserID.
EDIT: This is initially more work than just doing a (dare I write it?)
Int32.Parse(Request["userId])
in your view. However, the approach I have shown above is the preferred approach, since you will be leveraging the power of MVC's routing and binding capabilities. If you want to change parameter name someday, you just need to fix your route and the action's parameter name, as opposed to having to sort through all of your application searching for places where you pull values directly from the Request collection.
I believe you can get at it via the RouteData property of the request context. But a much easier way would be to just add a field for it to your model and have the controller set it there.
It depends upon your your routing scheme.
have look at these tutorial
Creating Custom Routes
mysite.com/user/show/1234
routes.MapRoute(
"User", // Route name
"user/show/{ID}", // URL with parameters
new { controller = "User", action = "show" } // Parameter defaults
);
above url wiil hopefully call, 'show' Action for 'user' controller and pass the ID, 1234
Change the default route in your global.asax.cs to -
routes.MapRoute(
"Default", // Route name
"{controller}/{id}/{action}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
Create a UserController with a Show method like this -
public class UserController : Controller
{
public ActionResult Show(int id)
{
var model = new UserViewModel {Id = id};
// Retrieve user from data layer and update model with other user details here
return View(model);
}
public ActionResult Edit(int id)
{
// Deal with edit action in here
}
}
public class UserViewModel
{
public int Id { get; set; }
}
In your aspx view, make sure that you page inherits from ViewPage<UserViewModel> by declaring in the page directive of your aspx view -
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<UserViewModel>" %>
Then you can create an edit link in your page like this -
<%=Html.ActionLink("Edit User", "Edit", new { id = Model.Id }) %>

Categories

Resources