i am new to MVC3 and our project needs something like:
http://www.abc.com/product_1/product_1_subpage/...
http://www.abc.com/product_2/product_2_subpage/...
right now, i have product_1 as a controller; product_1_subpage as an action of this controller, however, think about i have over 100 different products, i cannot keep create over 100 controllers for each single product, i need to do something on this structure, any idea?
thank you very much for your help, really appreciate any input.
You would probably want to have only a single Controller called Products, for all your products, instead of having to add a new controller for every product.
With custom routes, or the default routing for that matter, you would still be able to generate individual links for individual products. Also, if you were to use your approach with a new controller for every product (which you really shouldn't!), you would have to re-compile and deploy your application every time you want to add another product - which would be a pain to maintain.
It sounds like you should have a look at the tutorials about MVC provided by the .Net team to get some basic understand of MVC, and how to think about it.
Use custom routes:
routes.MapRoute(
"ProductsRoute", // Route name
"products/{productName}/{subName}/{id}", // URL with parameters
new { controller = "Product", action = "View", id = UrlParameter.Optional } // Parameter defaults
);
That would make the following work:
public class ProductController : Controller
{
// http://yourweb/products/goggles/xray/Elite2000
public ActionResult View(string productName, string subName, string id)
{
}
}
how about changing the format of the url a little to take advantage of the routing:
http://www.abc.com/product/subpage/1
Related
I have a two different projects that are actually two different websites when deployed, say WebA & WebB. The structure of both websites is the same in terms of the controllers and models they use. However, the front end is very different. They each have their own UI and own jquery plugins that work for them.
To reduce code duplication, I am proposing both those projects inherit from controllers in another referenced project[controllerDLL]. ControllerDLL is a project that will have all the logic for calling the business layer and returning json serialized model objects. WebA & WebB will each just have empty controller classes that just inherit from the base project[controllerDLL]. This way I feel the controller code is not duplicated.
Can anyone tell me a better way to achieve controller reuse other than the way I have proposed? Does asp.net mvc 4 provide any built-in way to do this better? Can I use AREAS here productively? Is DotNetNuke something I should look at? Or is my approach the best way forward? I am not looking for the how to move controllers into another project. I just want to know what my options are for achieving controller reuse.
Thanks.
Take a look at this SO question. You could keep all the views in the same project and use a custom view engine to find the views (based off a web.config setting).
For example you could have 2 folders /Views/WebA and /Views/WebB. The custom view engine could look up the web.config setting to find out which folder to use and search for the views there. This way you would not have to duplicate the controller code or move it into a separate project. It will just swap out one presentation layer for another at runtime.
You should be able to achieve this by implementing custom controller factory to instantiate the right controller class based on config settings.
You need to implement the interface System.Web.Mvc.IControllerFactory.The two methods in this interface are:
1.System.Web.Mvc.IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName) - To create the controller instance based on RequestContext and controller name.
2.void ReleaseController(System.Web.Mvc.IController controller) - Release the controller instance
Detailed information about using that interface is available
Since the difference between the two applications are the views, you don't need to have them as separate projects. you could have two sets of views and deploy the same project twice using different settings in the web config. To do this, you'll have to implement your own ViewEngine so that the controller can return the correct right views.
public class AppSettingViewEngine: RazorViewEngine
{
public AppSettingViewEngine()
{
string[] viewLocations = new string[] { };
if (ConfigurationManager.AppSettings["Site"] == "WebA")
{
viewLocations = new[] {
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
};
}
if (ConfigurationManager.AppSettings["Site"] == "WebB")
{
viewLocations = new[] {
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/{1}/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
"~/WebB/Views/Shared/{0}.cshtml",
};
}
else
{
//Default Settings
viewLocations = new[] {
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.cshtml",
};
}
this.PartialViewLocationFormats = viewLocations;
this.ViewLocationFormats = viewLocations;
}
}
Then you register it in your Application_Start as follows
protected void Application_Start()
{
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new AppSettingViewEngine());
}
I have a website that will service a few different countries. When a user hits the site, I'll use an IP locator tool to determine which country they are from. All of the countries will speak English so the views rendered will be the same, however pricing will be different as well as a few other things.
The routing I would want is something like:
mydomain.com/UK/Pricing
mydomain.com/Australia/Pricing
mydomain.com/UK/Features/SomeFeature
mydomain.com/Australia/Features/SomeFeature
You get the idea...
Can anyone provide me with the routing I'll need in the global.asax?
I've tried the following, but I don't want to have to create views for every country:
routes.MapRoute(
"Default",
"{country}/{controller}/{action}/{id}",
new { country = "UK", controller = "Home", action = "Index", id = "" }
);
Your route should be good enough, you simply need to add an argument to your Action methods to retrieve the country value:
public ActionResult Index(int id, string country) {
// your code here...
}
I'm WebForms you can also use
string country = Page.RouteData.Values["country"].ToString();
Not sure if this works for MVC too, but I would assume so.
Being kind of a newb to MVC 4 (or really any of the MVC's for ASP.NET) I cant help but feel theres more to the URL helper than what I'm seeing.
Basically I've read the tutorials on populating the attributes in a controllers methods using a query string in the URL.
I dont liek query strings though and prefer a sectioned "folder" like style.
Without much further adu, this is the sample URL:
http://something.com/DataTypes/Search/searchString
this approach is actually pretty safe as there will only ever be single worded searches
I have tried in the DataTypes controller
[HttpGet]
public ActionResult Search(String q)
{
ViewBag.ProvidedQuery = q;
return View();
}
and a few other small variations, right now im just trying to get the string to show up in the view but I dont seem to be getting anything there.
Is there anyway to inject the 3rd string in the url into an attribute?
If not, which URL helper class am I supposed to use to acquire the string data in the URL? Even if I have to parse the whole URL manually so be it, i just need to acquire that 3rd element in the URL as a string
Extremely n00b question im sure, but either im not finding this simple guide somewhere, or im not searching google correctly...
What you're missing is that the default route parameter name is "id". So you want to do this:
[HttpGet]
public ActionResult Search(String id)
{
ViewBag.ProvidedQuery = id;
return View();
}
If you don't want to use the variable name id, then you can modify your Route to look like this:
routes.MapRoute(
name: "Search",
url: "DataTypes/Search/{searchString}",
defaults: new { controller = "DataTypes", action = "Search",
searchString = UrlParameter.Optional });
If you don't want the string to be optional, then you can remove the last field from the defaults object.
you can use RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext)) to get the routedata
String URL to RouteValueDictionary
You need to look at the routing in the Global.asax.cs. For example for your case you could add a route to the routes collection like this:
routes.MapRoute("Search",
"DataTypes/Search/{q}",
new { controller = "DataTypes", action = "Search" }
);
Then the q parameter will automatically get mapped to your action. The default controller mapping is likely mapping it to "id".
For my Orchard project, I need some additional information from the user at registration time. (Say, First Name, Last Name, Pants Color). This information must be entered while registering and can not be deferred until later (as per client's orders).
I tried using the Profile and Extended Registration plugins to ask for those, but as far as I see, this only gives me optional fields to display in the registration form. Is there a way to present fields that are mandatory?
I also had a quick foray into overwriting the AccountController's Register method, as per this discussion, but I couldn't get it to work: The controller is in a different place, it can't be subclassed and even if I force it to, code is never executed. I presume they are using a much older version of Orchard.
So, in which direction should I go to create a mandatory field that is close to the Orchard philosophy? Should I create a new field type that rejects empty values maybe? (is that even possible)?
I wrote the ExtendedRegistration module because of that same need.
You need to create a custom part, e.g.: MyRegistrationPart.
Then you add that part to the User ContentType.
In your part just add the [Required] attribute (Data annotations) to any properties that are mandatory.
Registration will not succeed until those mandatory values have been filled out!
Hope it's clear now.
While this probably won't answer your question just wanted to point out that it is my understanding that you don't need to override/subclass the AccountController class. Instead you need to "overwrite" the Users/Account/Register route by adding your own with a higher priority. To do that you need to implement an IRouteProvider as part of our module. Since it's an IDependency it will be loaded and processed automagically at run time. Something like:
public class Routes : IRouteProvider
{
public void GetRoutes(ICollection<RouteDescriptor> routes)
{
routes.AddRange(GetRoutes());
}
public IEnumerable<RouteDescriptor> GetRoutes()
{
return new[] {
new RouteDescriptor {
// Make sure to be higher than the default
Priority = ##### PRIORITY HERE (int) ######,
Route = new Route(
"Users/Account/Register",
new RouteValueDictionary {
{"area", "#### YOUR MODULE AREA HERE ####"},
{"controller", "#### YOUR ACCOUNT CONTROLLER HERE ####"},
{"action", "#### YOUR REGISTER ACTION HERE ####"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "#### YOUR MODULE AREA HERE ####"}
},
new MvcRouteHandler())
}
};
}
}
In Area VIPUsers I have controller ProfileController and other controllers.
If I want every method in ProfileController to have parameter (id)
and every method in other controllers to have parameter (userid and id)
how would I map it?
Would it be
context.MapRoute(
"ProfileMapping",
"VIPUsers/Profile/{action}/{id}",
new {controller="ManageUsers", id=""}
);
and then map a default one for all the controllers?
context.MapRoute(
"Default",
"VIPUsers/{controller}/{action}/{userId}/{id}",
new {action="Index", userId="", id = ""}
);
and...that's it? I see it's working but then it's weird...
If I go to a page on ProfileController and give it two parameters after {action} (VIPUsers/Profile/SomeAction/4/4/), it'd use the second mapped route. Is that correct?
thanks
and if I want the url to always require a UserId, how do I specify that?
The behavior you are seeing is correct. You will want to implement some route constraints to further narrow things down:
http://www.asp.net/learn/mvc/tutorial-24-cs.aspx