A little background first as to why I need this. I am currently creating a CMS. If you imagine this CMS has a PageController which provides all the information a standard page needs, content, navigation etc.
Now the CMS can be amended for each client using it, and should a client require extra/different information in their pages I would like to override the default PageController with one tailored specifically for their needs.
This is what I have tried:
Base controller
namespace CMS.Core.Controllers {
public class PageController : Controller {
public virtual ActionResult DisplayHome() {
// Logic
return View();
}
}
}
Client specific controller
namespace CMS.ClientCore.Controllers {
public class PageController : Core.Controllers.PageController {
public override ActionResult DisplayHome() {
return Content("Client Home"); // Just for testing
}
}
}
Route
routes.MapRouteInLowercase(
"Home",
"",
new { controller = "Page", action = "DisplayHome" },
new[] { "CMS.Core.Controllers", "CMS.ClientCore.Controllers" }
);
The Error
The request for 'Page' has found the following matching controllers:
PCCMS.Core.Controllers.PageController
PCCMS.ClientCore.Controllers.PageController
The cause of the error is obvious, so is there an alternative method to override a controller/controller action?
You are approaching the problem in the wrong way.
Create a IContentProvider which is used by the PageController, and let the content provider figure out what content the current customer needs.
Related
I'm trying to wrap my head around MVC.NET 3.
I use a _Layout.cshtml as base (structure, navigation). As part of the layout I want to display two links used for changing language/localization.
These should be displayed and clickable no matter what page is viewed, and after changing the localization I want to reload the view that called the action. So the page that the customer is looking at will be reloaded, with new localization set.
One way is to copy and paste the localization-changing action in each of the sites controllers, but is there no easier and more elegant way?
I tried creating a specific controller that handles the localization changing, but can't figure out how to return the viewer to the previous controller.
Perhaps this is easier accomplished with jquery?
This is the DIV from the _Layout file with the language changing buttons. It calls the action in the current controller, which means I have to define it in each of the site's controllers. (The good thing is the view that is returned is always correct.)
<div id="top>
#using (Html.BeginForm())
{
<button id="sv_flag" name="localization" title="#Resources.Global.Sv_Flag_Hover" value="sv-SE" />
<button id="en_flag" name="localization" title="#Resources.Global.En_Flag_Hover" value="en-GB" />
}
</div>
I also tried using a specific controller for this, but cannot think of how I could return to the current view afterwards? Like so:
#using (Html.BeginForm("LocalizationAction", "LocalizationController"))
...
Edit
Now using the suggestion from Darin, I send in the controller and action values from the layout page:
#using (Html.BeginForm("SetLocalization", "Localization",
new { returnController = #ViewContext.Controller.ValueProvider.GetValue("controller").RawValue,
returnAction = #ViewContext.Controller.ValueProvider.GetValue("action").RawValue }))
...
But I cannot get the localization changes to work, my controller action looks like this:
public ActionResult SetLocalization(string localization, string returnController, string returnAction)
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(localization);
return RedirectToAction(returnAction, returnController);
}
You could pass a returnUrl:
#using (Html.BeginForm("LocalizationAction", "LocalizationController", new { returnUrl = Request.Url.AbsoluteUri }))
{
...
}
and inside your LocalizationAction redirect to this url:
public ActionResult LocalizationAction(string returnUrl)
{
... do your localization stuff and once you are done get back:
return Redirect(returnUrl);
}
obviously you could do a little checking before blindly redirecting. Things like whether the returnUrl parameter is not empty and whether it belongs to your domain. You may take a look at how the default AccountController does that once it authenticates a user.
return Redirect(Request.UrlReferrer.ToString());
public ActionResult Change(String LanguageAbbrevation)
{
if (LanguageAbbrevation != null)
{
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(LanguageAbbrevation);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(LanguageAbbrevation);
}
HttpCookie cookie = new HttpCookie("Language");
cookie.Value = LanguageAbbrevation;
Response.Cookies.Add(cookie);
return Redirect(Request.UrlReferrer.ToString());
}
I found a completely different (and much easier and elegant) solution to my problem.
I simply created a BaseController, that holds the action for changing the localization.
Then all controllers I add to the site inherit from this BaseController. This gives a single location for the code and does not require sending any return parameters, etc.
BaseController:
public class BaseController : Controller
{
[HttpPost]
public ActionResult Index(string localization)
{
Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(localization);
return View();
}
}
Each of the site's Controllers then only need to inherit from it, and then mind their own actions:
public class ApplicationsController : BaseController
{
//
// GET: /Applications/
public ActionResult Index()
{
return View();
}
}
...
I'm working with ASP.NET MVC 3. I'm kind of new to it. I think I'm starting to get the hang of it. But there is something that I'm trying to do, that I think makes sense, but maybe I'm wrong.
I'm trying to create an API around Order objects in my database. In order to get all of the orders in the system, I was going to expose an API that looks like the following:
/orders/
In cases where I wanted to get a specific Order, I would simply append an ID. In other words, the URL would look like this:
/orders/12345
In an effort to accomplish this, I created the following controller:
public class OrdersController : Controller
{
// GET: /Orders/
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
string result = "list of orders";
return Json(result, JsonRequestBehavior.AllowGet);
}
//
// GET: /Orders/{orderID}
public ActionResult Index(int id)
{
string result = "order:" + id;
return Json(result, JsonRequestBehavior.AllowGet);
}
}
In my AreaRegistration class, I have the following:
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"OrderList",
"{controller}/{action}",
new { action = "Index", controller="Orders" }
);
context.MapRoute(
"Order",
"{controller}/{action}/{id}",
new { action = "Index", controller = "Orders" }
);
}
When I attempted to access "/orders/", via the browser address bar, I get the JSON like I would expect. However, if I attempt to access "/orders/12345", I receive a 404. What am I missing?
Thank you
You need to also define proper routes in global.asax or use the default route which looks like {controller}/{action}/{id} where controller is defaulted to "Home", action is defaulted to "Index" and id is optional.
So /orders works because you have defined controller (orders), default action (Index) and missing id (which doesn't matter as it is optional)
But when you try /orders/12345 then you have defined controller (orders), action (12345) and missing id
So to make this work with only the default route the request should be /orders/index/12345
edit: for registering area routes you should use AreaRegistration class
İn my MVC3 project, there is plenty of TempData[] that I am using for passing datas between actions. And it works totaly perfect when I use Chrome. But in IE I can't get values of TempData[] items. if anyone knows whats the problem and how can I solve it?`
public class SomeController : Controller
{
public ActionResult SomeAction()
{
TempData["id"] = "someData";
return View();
}
}
public class AnotherController : Controller
{
public ActionResult AnotherAction()
{
string data = Convert.ToString(TempData["id"]);
return View();
}
}
`
You should never return a view from a controller action that stores something into TempData. You should immediately redirect to the controller action that is supposed to use it:
public class SomeController : Controller
{
public ActionResult SomeAction()
{
TempData["id"] = "someData";
return Redirect("AnotherAction", "Another");
}
}
public class AnotherController : Controller
{
public ActionResult AnotherAction()
{
string data = Convert.ToString(TempData["id"]);
return View();
}
}
The reason for this is that TempData survives only for a single additional request. So for example if inside the view you are sending an AJAX request to some controller action (no matter which) and then have a link in this view pointing to the target action, when the user is redirected to this target action TempData will no longer exist since it was lost during the AJAX request done previously.
If you need to store data for longer than a single redirect you could use Session.
If you need to store data for longer than a single redirect you should use Keep or Peek methods.
string data = TempData["id"].;
TempData.Keep("id");
or simply use,
string data = TempData.Peek("id").ToString();
Peek function helps to read as well as advice MVC to maintain “TempData” for the subsequent request.
First keep in mind I am new to nop/mvc, and despite my best effort can find no solution to this seemingly simple task.
I have been defining custom routes for a plugin I am making, and so far it's been going fine. Any routes I define have worked without issue (Example, I have set routes for "/Dealerlocator" and "Dealer/List")
The issue comes from the fact that there is an already an area defined for '/Admin", so when I try to set a custom route for something like "Admin/Dealer", from what I can tell my route is being resolved by the area and not my custom route. It looks like my controller is never reached, as it's in a different namespace then the one the area route uses, and I get a "The resource cannot be found." error.
So what I'd like to happen is when I go to "Admin/Dealer", it ignores the route set in the area in this one cause, and uses the route I define in the RouteProvider class.
It was suggested using DataTokens would fix this problem. However I cannot get them to work.
My Plugin routing code:
public partial class RouteProvider : IRouteProvider
{
public void RegisterRoutes(RouteCollection routes)
{
var route = routes.MapRoute(
"Nop.Plugin.Misc.DealersAdmin",
"Admin/Dealer",
new { controller = "DealerAdmin", action = "Index" },
new[] { "Nop.Plugin.Misc.Dealers.Controllers" }
);
route.DataTokens.Add("Area", "Admin");
}
}
Nopcommerce's admin area routing:
public class AdminAreaRegistration : AreaRegistration
{
public override string AreaName
{
get
{
return "Admin";
}
}
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { controller = "Home", action = "Index", area = "Admin", id = "" },
new[] { "Nop.Admin.Controllers" }
);
}
}
I tried setting a condition on the area registration to not match if the controller is named "Dealer", and that seems to work. But I cannot change the AdminAreaRegistration class as it's part of the core nop framework. I would like to see all the work done in the RouteProvider class. Maybe there is a way to set the priority on my route higher so it is the first one resolved? Thanks.
I have also come across this issue a while back, it is to do with route priority. This post helped me alot.
Regarding your comment - There is no reason why you couldn't do so, but alternatively, you might have more luck defining your route as;
context.MapRoute(
"DealerAdminDefault",
"Dealer/Admin/{action}/{id}",
new { controller = "DealerAdmin", action = "Index", id = UrlParameters.Optional }
);
Hope this helps,
Matt
I'm new to MVC and Google hasn't been much help so I will ask here.
What I'm trying to do is simple (I would had thought) I want to pass a string to the index method but it generally looks like:
http://mydomain.com/home/index/mystring
and I want:
http://mydomain.com/mystring
How do I go about that?
You could define the following route in Global.asax:
routes.MapRoute(
"MyStringRoute",
"{*mystring}",
new { controller = "Home", action = "Index" }
);
which will invoke the Index action of the Home controller:
public class HomeController : Controller
{
public ActionResult Index(string mystring)
{
return View();
}
}