In struts 2, the action to class mapping can be done in 2 ways:(Please pardon the wrong syntax)
action = "action1" class = "class1" results = "results1"
action = "action2" class = "class2" results = "results2"
and so on for each action = 1 , 2 ....n i.e. 1 action class per request
Or:
action = "action1" class = "class1" results = "results1" method = "method1"
action = "action2" class = "class1" results = "results1" method = "method2"
and so on. i.e only 1 action class for all requests. But each action has a separate method.
Is there any best practice which to use when?
Think of it as you would a general Java problem. Should I create a new class to address this issue or should I create a new method to address this issue? That is, is this a new thing or better expressed as a behavior of an existing thing.
If you have a page which will display employees, then that deserves an action class. If you have another page that displays companies, that deserves another action class. I like to put CRUD and certain Ajax features into the same class because I think I move around less that way.
It wouldn't however make sense to have one action class loaded with methods mashing together unrelated ideas, well it wouldn't be Java thinking anyways.
Really one action to one class is a personal preference thing but I think you should be consistent what ever you do. Projects that don't require much crud or ajax perhaps would use methods as actions so rarely that perhaps it would be better to just consistently avoid them.
Related
In our codebase we have a closure in a controller that looks like this:
def doComputation = {
def someObject = SomeObject.read(params.long("objectId"))
//some irrelevant logic
def response = [importantVariable : importantInteger, someOtherVariable : someOtherInteger]
render response as JSON
}
Of course this is generally called from the .gsp page, but is there a way to call it from a closure in a different controller? Like so:
def someOtherClosure = {
//some more irrelevant logic
def responseFromOtherClosure = doComputation(objectId : someLong)
theValueIWant = responseFromOtherClosure.importantVariable
//more logic
}
but is there a way to call it from a closure in a different
controller?
There is.
The closures are just properties in the controller class and they are public. Controllers are configured as Spring beans so you could (you shouldn't) inject a controller into another controller and then reference the closure properties from there.
I think that answers your question but there are no good reasons to do that. The right solution will depend on some particulars but if you have 2 controller actions that need to share some logic, best practice is to move that logic into a service and inject that service into all the controllers that need it.
Depending on what you are trying to accomplish, instead of invoking a controller's code directly from another controller (again, no good reason to ever do that) you might want the first controller to issue a redirect or a forward which will lead to the second controller.
I'm trying to get some common properties to all my Views that derive from ViewModelBase. Except I ran into a sort of Catch-22...
The common properties of my ViewModelBase are: User and Environment (there are more, but for this question these two would suffice).
Right now, I have a global filter that assigns these two properties - the ViewModelBase.User property is assigned from BaseController.User, which was previously assigned by an Authorization Filter.
It works well in all cases, but this: The Authorization Filter some times based on business logic decides to redirect the user to the "not yet approved" page. This is done like so:
var notApprovedView = new ViewResult();
notApprovedView.ViewName = "NotYetApproved";
filterContext.Result = notApprovedView;
Except, in this case, the ViewModel does not yet have the common property that was supposed to be assigned by the Global filter, which results in a run-time exception.
What's the proper (i.e. best practice, not hack) way to deal with a situation where I need common properties assigned and some of the common properties may change depending on authorization?
Thanks.
I think I may have missed the forest for the trees on this one... Sorry.
I could just do the following in the Authorization Filter:
var notApprovedView = new ViewResult();
notApprovedView.ViewName = "NotYetApproved";
// add this line
notApprovedView.Model = new ViewModelBase(){User = this.user};
filterContext.Result = notApprovedView;
I have a client who wishes to use a URL naming convention along the lines of:
/{subjectarea}/{subject}/{action}
Which is fine - this works brilliantly, with one controller per subject area, and having the action after the id (subject) is no issue at all.
However, it then gets complicated, as the client then wants to further continue the hierarchy:
/{subjectarea}/{subject}/{action}/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
I have a controller for the tightly related subject (its just another subject area) which handles all of the admin side, but the client insists on having the public view hung off of the parent subject rather than its own root.
How can I do this while avoiding breaking the entire principals of MVC, and also avoiding re-implementing a ton of ASP.Net MVC provided functionality in my subject area controller just to be able to handle the related subjects from that same controller?
Is it possible to somehow call the related subjects controller from within the parent subject controller, and return the resulting view (as this would keep the separation of functionality for the subjects to their own controllers)? If that is possible, it would solve a heck of a lot of issues with this.
Here is the solution which solves my given issue - hope it solves someone elses.
As mentioned in my comment to Robert Harvey, all I actually need is another route which doesn't use the first two or three components as the controller, action and id, but instead takes those values from later on - if you hang this off of a static value in the route as well, its much easier to do.
So, here is the url I decided on to simplify the route:
/{subjectarea}/{subject}/related/{tightlyrelatedsubject}/{tightlyrelatedsubjectvariables}/{tightlyrelatedsubjectaction}
The route which satisfies this URL is as follows:
routes.MapRoute(
"RelatedSubjects",
"{parentcontroller}/{parentsubject}/related/{controller}/{id}/{action}",
new { controller = "shoes", action = "view", id = "all" }
);
On the subsequent controller action, I can ask for parameter values for parentcontroller and parentsubject so I can filter out the related item to just be specific to the given parent subject - problem solved!
This route needs to be above the ones which just deal with the first two values, otherwise you run the risk of another route map hijacking the request.
I could do this entirely without the /related/ static portion as the route could easily match on number of values alone, and infact I may indeed do so - however, I consider it better for later administration if there is a static item in there to confirm the use of the route.
I hope this helps someone!
One way you can do it is specify a wildcard route (notice the asterisk):
routes.MapRoute("subjects", "{action}/{*path}",
new { controller = "Subjects", action = "Index" });
This allows the controller to receive the entire path string after the action.
You can then obtain the hierarchy of subjects in the controller method like so:
string[] subjects = path.Split('/');
Once you have that, you can do anything you want, including dispatching different subjects to different handling methods for processing.
My url http://server/region/section/item
Now if someone goes to http://server/us/ I want to display a page to choose a section
if someone goes to http://server/us/beer/ I want to display a list of all beers.
My question is should I just have 1 route with defaults and then return different views depending on how much the URL is filled in or should I create multiple routes going to the same controller different actions? or even different controllers, I just want to know what the best practice is.
The typical route looks like this:
http://www.domain.com/controller/action/id
[domain/controller/action/id]
In your case, it's short one part:
http://server/us/beer
[domain/controller/action?/?]
As Robert Harvey said, you wouldn't want to have an action for every product. So how about something like:
http://server/us/product/beer
[domain/controller/action/id]
domain = server
controller = us (tho, this doesn't seem like it would be a good name for the controller)
action = product
id = beer
Then you'd develop a product view that would show the beer data to your visitors.
This isn't architect-ed very well, but without knowing your situation it would be difficult for anyone to answer this. I hope this helps though.
In your particular case, "beer" can probably be a filter to a controller action, rather than another controller action, in which case you need only one route entry.
It doesn't seem wise to create a strategy that will require you to add new routes, controller methods and/or views every time you add a product category. Unless, of course, you want to highly customize each category's look and behavior.
Is it possible in ASP.NET MVC via some extension/override points to allow a "delegate field" to be used as an "action"?
Something like:
using System;
using System.Web.Mvc;
namespace Company.Web.Controllers
{
public class SwitchboardController : BaseController
{
public Func<ActionResult> Index, Admin, Data, Reports;
public SwitchboardController()
{
// Generic views
Index = Admin = Data = Reports =
() => View();
}
}
}
I know I'm a little hell-bent for this one but if this is possible it'd open up many new ways of making actions. You could, for example, have Django-style generic views in MVC with only a single line of code to define the action or have different ways to factor duplicate logic across multiple controllers.
I'm not quiet sure where would be the place to slap this logic into or how much work would be required to alter something so fundamental in the framework.
You will probably have to build your own Controller factory. This class builds controllers, and implements IControllerFactory. You can inherit from DefaultControllerFactory. Override CreateController() to return your own IController.
Register your controller factory in Application_Start() of MvcApplication using this line:
ControllerBuilder.Current.SetControllerFactory(typeof(MyControllerFactory));
In your implementation of IController, override the Execute method. You can use the RequestContext to decide which delegate to invoke. It would probably be easiest to inherit from ControllerBase, and override Execute in there if you don't want to fully implement IController.
The RequestContext passed into Execute carries a RouteData object. This is a dictionary populated by the routing engine that tells you what action should be invoked, and any parameters. You can get the action name like this:
//context is a RequestContext object passed to IController.Execute()
string actionName = requestContext.RouteData.Values["action"];
You could even define your action as a dictionary, and just pull them out once you get the action name.
One last thing, normal action methods return an ActionResult, which the framework uses to decide which view to render. Once you execute your delegates, I think you'll have to set some stuff manually in your special base controller. I'm not exactly sure what to set or how to get your View executed from here without cracking open the MVC source.
Good luck! This looks like an interesting idea.
As you seem to be implementing a BaseController in your code sample, if you override the Execute (from the IController) you'll be able to interpret the request => action however you like.
No, it isn't. The base controller is looking for methods and not for fields to dispatch an action.
EDIT:
Sorry, I was a bit fast and fixed to the standard classes provided.
You can do that but you have to overwrite the Execute Method in your controller or implement and provide your own IActionInvoker to dispatch the action to fields. Look into the post action processing in detail. It explains the dispatching in detail.