I am creating a Grails application which has an input page with text fields. Here the user can type in the data and on submit, the control goes to the action in controller.
Here I get the value of the form data using params.empName etc.
But the scope of this data is very small and doesnt get carried on if I do a redirect from the current action to another action.
Is there a way to increase the scope of the variables?
I am now to convert this to service oriented architecture. Therefore Is there a way to access these data in the service as well?
Please advice.
Thanks,
Megs
You can add...
params: params
...as an argument to the redirect, so that the incoming params are sent along with the redirect.
I don't think there's a built-in way to increase the scope. This is probably a Good Thing.
If you're redirecting in controllers, you should simply pass along the necessary parameters via the redirect() params dynamic property. Example:
def formHandler = {
// do stuff with params
redirect(action: 'anotherAction', params: params)
}
If you need scope to span multiple requests, e.g. if you're having a multi-step form entry given to the user, you might look into using web flows to persist state between requests.
For services, you're better off just passing down what you need as arguments to the service method, rather than exposing params. Example (similar to the Accessing Services section here):
// service
def myServiceMethod(def foo, def bar) {
// do stuff
}
// controller
def myService
def myControllerAction {
myService.myServiceMethod(params.foo, params.bar)
}
Exposing parameters from the controller to the service layer would break the layer-oriented approach Grails is trying to provide you; i.e. the "model" and "controller" components (of MVC) would be more tightly coupled.
I would also take a look at chaining actions as a way to pass the model information
http://www.grails.org/Controllers+-+Redirects
Related
I'm currently using MVC5.
Imagine the scenario where one controller ActionA does its work and the redirects to another controller ActionB but also wants this second method to display a message on its related view.
If Controller ActionA sets the ViewBag.Message and then calls RedirectToAction, when ActionB starts, the value of that Message is gone.
What's the best way to pass a message from one action controller to another, without using Session ??
You can use TempData:
Action A:
TempData["Message"] = "Hi";
Action B:
var message = TempData["Message"];
Once you call the getter in Action B, the information will be automatically removed from memory.
This article is a really good explanation of the various persistence techniques available in ASP.NET MVC.
You say no session, but TempData can be a good place depending on what you're passing. It's essentially Session except that the item will be removed from Session once you've accessed it. TempData actually uses session by default unless you write your own provider.
Passing it as an option in the query string, or passing it as a parameter in the routing (similar to what AJ says)
Other than that you've got the standard run of the mill options that are provided in ASP.NET. HttpContext.Items or maybe HttpContext.Cache. But both of those are shared across the entire application domain so management can get tricky.
Remember, web is supposed to be stateless. So if it's really important for that message to get there, you probably want to put it in the URL somehow (query string or routing), or use a database.
I would put an optional argument on the method signature for ActionB, for example:
public ActionResult ActionB(string message = "")
Note that optional parameters need to be last in your parameters list.
Based on another question:
Creating a Child object in MVC4 - Parent's information not being passed to Create() controller
Does MVC provide a mechanism to send data from the HttpGet Create() to the HttpPost Create() without going through the client? If I need to send some data to the Post method that is meaningless to the client, how can I avoid cluttering the Views and over-exposing model properties to potential attackers?
Your GET and POST actions are just methods on a class. It really doesn't sound like there's any reason to use POST here, if your only concern is to execute a block of code under certain conditions.
Change your POST (drop the attribute) and make it a private method so it is inaccessible to the client. In your GET, do whatever checks you need to do, then invoke the method.
If you do need to expose the POST, refactor the code in question out to a seperate private method that you can call from either GET or POST. A better implementation would be a separate class with the method located there for reuse/testing/SoC.
Just a caution if you are working with a DB here...while there are some legitimate reasons to write to the DB during a GET, note that this is not the indempotent nature of the GET in most circumstances (http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html).
Cheers.
Yes - as I mentioned in the other answer you can use TempData - however you don't need this. You are using nothing but an id and a name from your entity, just pass only those in the view model. You don't need the full entity.
On the server side ensure your user has access to those records. For example if I was editing a Customer record, I'd ensure the current user has access via something like:
var currentUsersCompanyId = GetCurrentUserCompanyId();
ctx.Customers.Single(o=>o.CustomerId = customerId and currentUsersCompanyId == customerId)
There are a variety of ways to do this based on how you control permissions - and third party platforms for .net such as Apprenda that help do this more automatically behind the scenes.
I created a static scaffolding for my domain class and got a controller for that domain class. The create action of the controller looks like:
def create() {
[userInstance: new User(params)]
}
I wonder why the line:
[userInstance: new User(params)]
has been added. Obviously when the create action is invoked, there wont be any params - so why would this line have been added?
Obviously when the create action is invoked, there wont be any params
Not necessarily - imagine a situation where you want to pre-populate a couple fields in the form of the create view. You could use:
/app/user/create?username=myusername
Which would result in the view's userInstance having a populated username field for display in the form.
This is fundamentally by Spring, the action is called 'binding' and is the action of tie form elements from one jsp(gsp in this case) to the properties of an object and viceversa.
To tie an object to a form, well, you should create it first, how can ypu tie a null object? it's not possible, that is why the new ClassObject(...)
After that in Groovy we have POGO's, and one feature of POGO's is the ability of initialize them with a map, for example:
new User(name:'John',lastname:'Zuñiga')
But in this case there's a lil' of groovy magic with that 'params' object. That comes from Groovy Servlets or Groovlets. How can you obtain a param with Java incoming from a form? Well, with request.getParam("param_name"), but in this case with Groovy you receive a params object, this params object is a map, a Groovy map...Uhm, one second...
If POGO's in Groovy is able to receive a Map as constructor, and the params object is a Map...maybe...oh coolness I can put that map in the constructor of my object, and after Spring do the binding to the form with this new Object, so this object is travelling in actions from this controller so it comes with the properties populated.
I hope this explanation be clear, if you have questions, I'm here...
Regards
There could be params, although in general there wouldn't be.
It allows pre-loading of values, which may be helpful sometimes, including re-displaying the create form.
Many of the available controller methods (chain, forward, redirect) take a map which can include keys such as:
id
params
model
A couple of questions about these:
Is 'id' just an alias for a request parameter named 'id'? In other words, is there any difference between:
chain(controller: "member", action: "showProfile", params: [id: memberId])
and
chain(controller: "member", action: "showProfile", id: memberId)
The chain method (possibly among others) allows passing a model and/or params (map) from controller action A to B. Practically speaking, what's the difference between passing data from action A to B via the params and model maps? Also, if the data is passed in the model map, how do I access it in controller action B?
Everything Burt said is correct. In addition, the reason that you'd want do do a chain (if you have a model) or a redirect (if you don't have a model to keep) is because both of those methods return a 302 redirect response to the browser. The browser then knows to ask for the next page.
It then has the correct url in the header for the resulting page, rather than the url from the page where the original request was from.
This pattern is very useful after a POST of information as it avoids all kinds of trouble with bookmarking, and resubmitting of information if the user hits refresh on the resulting page.
Ex: if you're saving a Book and you want to render the list page if the book is successfully saved. If you just call "controller.list()" in your method, it will show the user the list of books that gets rendered, but the url bar will still say ".../book/save". This is not good for bookmarking or reloading. Instead, calling redirect/chain will send the 302 response to the browser telling it to ask for the ".../book/list" page, which it does. All of your variables (your model and other flash messages) are in flash scope so they're still available to your model/view to use and everything is happy in the world.
This pattern is called Post/Redirect/Get.
'id' comes from UrlMappings entries such as "/$controller/$action?/$id?" - see http://docs.grails.org/latest/guide/single.html#urlmappings for usage.
Params are querystring parameters or form post parameters, accessed in non-Grails apps using "request.getParameter('foo')" but simplified in Grails as "params.foo". The contents of the model map are stored in the Request as attributes, accessed in non-Grails apps using "request.getAttribute('foo')" but simplified in Grails as "request.foo" or more typically accessed directly in GSPs, e.g. "${foo}".
I'm experimenting with MVC, and my question is - where I had Page_Load logic in Master Pages with WebForms, where should it go in MVC? Here's the business case:
Different Host Headers should cause different Page Titles to be displayed on the site's (one) Master Page, therefore all pages. For example, if the host header is hello.mydomain.com, the page title should be "Hello World" for all pages/views, while goodbye.mydomain.com should be "Goodbye World" for all pages/views.
If the host header is different than anything I have in the list, regardless of where in the application, it should redirect to /Error/NoHostHeader.
Previously, I'd put this in the MasterPage Load() event, and it looks like in MVC, I could do this either in every controller (doesn't feel right to have to call this functionality in every controller), or somewhere in Global.asax (seems too... global?).
Edit: I have gotten this to work successfully using the Global.asax method combined with a Controller for actually processing the data. Only problem at this point is, all of the host header information is in a database. I would normally store the "tenant" information if you will in a Session variable and only do a DB call when it's not there; is there a better way to do this?
There is no 1:1 equivalent in MVC for a reason, let's just recapitulate how to think about it the MVC way:
Model: "Pages of this site are always requested in a certain context, let's call it the tenant (or user, topic or whatever your sub domains represent). The domain model has a property representing the tenant of the current request."
View: "Render the page title depending on the tenant set in the model."
Controller: "Set the tenant in the model depending on the host header".
Keep in mind that what we want to avoid is mixing controller, view and business logic. Having controller logic in more then one place or a place, that is not called "controller" is not a problem, as long as it remains separated.
And now the good thing: You could do this "MVC style" even with Web Forms, and the solution still works with ASP.NET MVC!
You still have the request lifecycle (not the page lifecycle), so you could implement a custom HttpModule that contains this part of the controller logic for all requests. It handles the BeginRequest event, checks for the host header, and stores the tenant to something like HttpContext.Current.Items["tenant"]. (Of course you could have a static, typed wrapper for this dictionary entry.)
Then all your model objects (or a model base class, or whatever is appropriate for your solution) can access the HttpContext to provide access to this information like this:
public string Tenant
{
get { return HttpContext.Current.Items["tenant"]; }
}
Advantages:
You have separated cause (host header) and effect (rendering page title), improving maintainability and testability
Therefore you could easily add additional behavior to your domain model based on this state, like loading content from the database depending on the current tenant.
You could easily make more parts of the view depend on the tenant, like CSS file you include, a logo image etc.
You could later change the controller logic to set the tenant in the model not only based on the sub domain, but maybe based on a cookie, a referrer, search term, user agent's language, or whatever you can think about, without modifying any of your code depending on the model.
Update re your edit: I don't like the idea of holding the state in the session, especially if your session cookie might apply not only to each sub domain, but to all domains. In this case you might serve inconsistent content if users visted another sub domain before. Probably the information in the database that is mapping host headers to tenants won't change very often, so you can cache it and don't need a database lookup for every request.
You could create a base controller that supplied the correct ViewData to your MVC Master Page View, then derive each of your actual controllers from that one. If you put the logic into the ActionExecuting method, you should be able to generate an exception or redirect to an error page if necessary.
You are thinking too "WebForms" and not enough MVC. A master page is just a wrapper of your view, and it should only contain layout html. You can send stuff to your master, but it's a one way road and you should strive for agnostic views. Bottom line: forget about the events that WebForms had as they aren't going to be used here.
Since you are dealing with Host headers I suppose you could put it in the Global.asax...great now I'm confused :P
Stolen code from http://forums.asp.net/t/1226272.aspx
protected void Application_BeginRequest(object sender, EventArgs e)
{
string host = string.Empty;
if (this.Request.ServerVariables["HTTP_HOST"] == this.Request.Url.DnsSafeHost)
{
host = this.Request.Url.DnsSafeHost;
}
else
{
Regex regex = new Regex("http://[^/]*.host/([^/]*)(/.*)");
Match match = regex.Match(this.Request.Url.AbsoluteUri);
if (match.Success)
{
host = match.Groups[1].Value;
Context.RewritePath(match.Groups[2].Value);
}
}
// Match the host with the portal in the database
...
}
What you need is here http://www.asp.net/mvc/tutorials/passing-data-to-view-master-pages-cs