If I want to share an object between multiple methods in a controller, is it wrong to create an instance var? E.g.
class MyController {
def index() {
def user = verifyUserLogin()
[messages:getMessages(user)]
}
private verifyUserLogin() {
...
return user
}
private getMessages(user) {
...do something with `user`...
return messages
}
}
would become
class MyController {
private user
def index() {
verifyUserLogin()
[messages:getMessages()]
}
private void verifyUserLogin() {
...
this.user = user
return
}
private getMessages() {
...do something with `user`...
return messages
}
}
Grails controllers are not singleton they are created for every request so it should not be any issue to have instance level variables.
However I personally prefer to not to declare instance level variables as much as possible, because when code starts becoming lengthy it becomes difficult to figure out the flow of the code that initializes and uses the variables.
I rather choose to pass them as parameters. However there's no such rule of thumb and it highly depends on the problem at hand.
The traditional way - for Servlets at least - of storing state between controllers, i.e. passing information between requests is to use the session object.
class MyController(){
def index(){
session.user = getUser()
}
def postex(){
def user = session.user
}
}
In grails 3.2.1 the default scope of controllers changed from "prototype", controller instance per request, to "singleton", one instance of each controller.
This made instance variables in controllers no longer safe unless you change the scope!
https://docs.grails.org/3.2.1/ref/Controllers/scope.html singleton
https://docs.grails.org/3.2.0/ref/Controllers/scope.html prototype
If you do want to use instance variables in a controller change the scope static scope = "prototype".
Rather than use instance variables you can set variables on the request (as request attributes). request.user = user Note that these will persist for the life of the request (visible to all interceptors and views traversed by the request).
Related
I've got several properties in my domain class. However, I only want few of them to be set via the params object. What is a good way to do this?
Example:
Domain
class Color {
String name
String shade //don't want this set by params
}
controller
class ColorController {
def save() {
json {
def c = new Color(params?.color)
c.save(flush: true)
//..more code
}
}
}
If someone sends a request like:
{"color":
{name: "red",
shade: "light"
}
}
then user can change the shade property. How can I stop this?
You could probably do one of a couple of things:
If it is many properties, create a transient beforeInsert() {} and/or transient beforeUpdate() {} method in your domain class and handle setting (or not) the properties.
If only a few, override the setters in the domain class.
Since Groovy makes me not want to mess with getters and setters unless I absolutely have to, I usually use the beforeInsert and beforeUpdate methods.
Grails provides a bindData method on the controller to give you fine grained control of data-binding. For your example you could write this as:
class ColorController {
def save() {
json {
def c = new Color()
bindData(c, params, [include: 'name'])
c.save(flush: true)
//..more code
}
}
}
In this case, only the 'name' field would be set on the c instance before attempting to save.
If you want to to additional validation on the incoming params, I would also suggest looking into using a Command Object for the data binding.
I've to display check box checked or not based on some logic which is more than 4 lines of groovy code ...
I don't like to write it in GSP page..is there any taglib or some way I can extract the logic out of GSP page.
where I could access model boject and request objects.
You have (at least) 3 options:
Model attribute
If you're only doing the complex logic for a single page or a single controller, do the logic in the controller method and pass the boolean result to the view through a boolean:
// in your controller
def myAction() {
[shouldDrawCheckbox: shouldDrawCheckBox(...)]
}
private boolean shouldDrawCheckBox(/* info for decision making */) {
// decision making
}
Service method
If you're going to access this identical logic from several controllers, you can extract the shouldDrawCheckBox method into a service and again pass the result through the models.
class MyController {
def myService
def myAction() {
[shouldDrawCheckbox: myService.shouldDrawCheckbox(...)]
}
}
class MyService {
boolean shouldDrawCheckBox(...) {
// logic!
}
}
Custom Taglib
If you want to avoid passing the decision through the model, or if the logic is more generally applicable, you can create a custom taglib.
class MyTaglib {
static namespace = "my"
def myCheckbox = { attrs ->
// extract decision info from the attrs
// perform logic with info
if (shouldDrawCheckbox)
out << g.checkbox(attrs: attrs)
}
}
}
In your view:
<my:myCheckbox whateverYourAttribsAre="value" name="..." value="..."/>
A TagLib is a good palce to put your logic, you can pass what you need as attributes and do your test:
class MyTagLib {
static namespace = "my"
def somecheckbox= { attrs ->
def model = attrs.remove('model')
if() { //tests goes here
//you can also test if you need to mark the checkbox as checked
if() {
attrs.checked = "checked"
}
out << g.checkbox(attrs: attrs) //remaining attrs will be applied to the checkbox
}
}
}
myview.gsp
<my:somecheckbox model="${model}" name="checkboxname" value="${checkboxValue}" />
Ideally the logic should go to the controller which renders the gsp and sets a flag in the model object. If the gsp is a child of a template, then the flag has to pass through. DOM manipulations in view layer is not ideal when we have appropriate binding framework available in grails.
Use the g: namespace. http://grails.org/doc/2.2.x/ref/Tags/checkBox.html
<g:checkBox name="myCheckbox" value="${condition}" />
It doesn't get any simpler then this. All the logic should be done inside the controller.
All the data you need on a page can be passed by the controller. Just return a Map.
class MyController {
def index() {
def someCondition = true
[request:request, condition:someCondition]
}
}
I am looking for ways on how to cleanup my Grails controller code. In various controllers i more or less have the same logic..
get the object
check if it exists
etc..
Is there a suggested way on making controller actions reuse common code?
--- solution ---
All answers to the question have contributed to the solution we have implemented.
We created a class that is used in our controllers using the Mixin approach. One of the methods that the mixin exposes is the withObject method. This method takes the domainname from the controller and uses this a base for the method. This behaviour can be overridden of course!
def withObject(object=this.getClass().getName()-"Controller", id="id", Closure c) {
assert object
def obj = grailsApplication.classLoader.loadClass(object).get(params[id])
if(obj) {
c.call obj
} else {
flash.message = "The object was not found"
redirect action: "list"
}
}
So all answers have contributed to the solution! Thanks a lot!
I always pull out this blog post when this question comes up:
http://mrpaulwoods.wordpress.com/2011/01/23/a-pattern-to-simplify-grails-controllers/
Basically you have a private helper for various domains in your controllers.
private def withPerson(id="id", Closure c) {
def person = Person.get(params[id])
if(person) {
c.call person
} else {
flash.message = "The person was not found."
redirect action:"list"
}
}
The way you code the getter is very flexible and a typical use for me (that is not covered in the blog) is for editing etc.
I normally code this way (i like the pattern for its clear division and readability):
def editIssue() {
withIssue { Issue issue ->
def issueTypes = IssueTypeEnum.values().collect {it.text }
[issueTypes:issueTypes,activePage:"issue", issue: issue]
}
}
def doEditIssue(IssueCommand cmd) {
if(cmd.validate()) {
withIssue { Issue issue ->
issue.updateIssue(cmd)
redirect(action: "show", id: issue.id)
}
}
else {
def issueTypes = IssueTypeEnum.values().collect {it.text }
render(view: "edit", model:[issueTypes:issueTypes,issue:cmd,activePage:"issue"])
}
}
With my getter helper being:
private def withIssue( Closure c) {
def issue = Issue.get(params.id)
if(issue) {
c.call issue
}
else {
response.sendError(404)
}
}
I do think that the mixin method (very similar to the 'extend a common abstract controller' way) is nice too, but this way gives two advantages:
You can type the helper, like you see I do in the closure giving you access to the methods etc in STS/IDEA (not tested Netbeans)
The repetition is not very high, and the ability to change the getter (to use for example BarDomain.findByFoo(params.id) etc)
In the view I bind to edit() I just put an id="${issue.id}" in the <g:form> and it works seamlessly.
I wouldn't recommend inheritance for that, as you can't spread generic methods in several super classes. Your abstract class would quickly become messy if you have many controllers. You can't use composition (for instance using a Service) because you don't have access to response, render, or params directly from there.
The approach I use is to inject generic methods via Mixins.
#Mixin(ControllerGenericActions)
#Mixin(ControllerUtil)
class BookController {
def show = &genericShow.curry(Book)
def exists = {
render(idExists(Book))
}
}
The first action show uses a generic method in ControllerGenericActions.groovy, with an argument binded to it. The second use of a mixin idExists method is inside a controller action.
Here is an example code for src/groovy/ControllerGenericActions.groovy
class ControllerGeneric {
def genericShow(Class clazz) {
render clazz.get(params.id) as XML
}
}
and in src/groovy/ControllerUtil.groovy
class ControllerUtil {
def idExists (Class clazz) {
return clazz.get(params.id) != null
}
Not very useful in this case, but you get the idea.
Implement abstract controller with common methods (use 'protected' directive) and extend from it your real controllers. Do not use 'get' and 'set' words at the beginning of this method's names. Not good, but it works.
I have a service to get and set the user in the session. I would like to pass in some user information to each view if there is a logged in user and thought a filter would be the best way so I don't have to duplicate that in every controller/action. When I run the app, it get this error:
Error creating bean with name 'userService': Scope 'session' is not active for the current thread
My filter looks like this:
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}
I know I can just ultimately access the user through session.user, but I want to be able to call userService.isLoggedIn() which I can't easily through the view. So is there a way to inject the service into the filter, or should I just create a taglib to wrap userService.isLoggedIn()?
It looks like the problem is your userService is scoped to the session, and there is not necessarily a session at the time the attempt to inject the service into the filter happens.
If your userService is necessarily session-scoped then you need to use a scoped proxy in your spring config. E.g. in grails-app/conf/spring/resources.groovy:
import com.your.service.UserService
...
userServiceSession(UserService)
{ bean ->
bean.scope = 'session'
}
userServiceSessionProxy(org.springframework.aop.scope.ScopedProxyFactoryBean)
{
targetBeanName = 'userServiceSession'
proxyTargetClass = true
}
Then rename your injected variable in your SecurityFilter:
def userServiceSessionProxy
(and obviously rename where you use it elsewhere in the class).
What this should do is inject the proxy at injection time, but only go to the actual service at the time the filter is executed (when there is a session).
Note: not sure whether doing this still lets other places where there will be a session (e.g. controllers) still reference the service as 'userService', if not you might be able to rename userServiceSession to userService in resources.groovy (and update targetBeanName accordingly).
It looks like there are some problems with services in filters. This bug might point you in the right direction: https://jira.grails.org/browse/GRAILS-5982
You can also try to get a reference to your service through the ApplicationContext. Here's an example: How do I get an instance of a Grails service programmatically?
import org.codehaus.groovy.grails.web.context.ServletContextHolder
import org.springframework.context.ApplicationContext
import org.springframework.web.context.support.WebApplicationContextUtils
class SecurityFilters {
def userService
def filters = {
setUser(controller:'*', action:'*') {
before = {
def servletCtx = ServletContextHolder.getServletContext()
ApplicationContext applicationContext = WebApplicationContextUtils.
getRequiredWebApplicationContext(servletCtx)
userService =applicationContext.getBean('userService')
if (userService.isLoggedIn()) {
request.user = userService.getUser()
} else {
request.user = null
}
}
}
}
}
I'm missing something....
I have a Grails webflow that looks like this:-
def childFlow = {
start {
action {
def targets = []
Target.list().each {target ->
targets.add(new TargetCommand(name: target.name, id: target.id))
}
log.debug "targets are $targets"
[children: targets]
}
on('success').to('selectChild')
}
...
TargetCommand is serializable. but I get this error:-
Caused by: java.io.NotSerializableException: com.nerderg.groupie.donate.Target
For some reason the "target" object that is inside the Target.list().each {} closure is getting put into the flow scope, and I can't figure out how to mark it as transient.
I have some code in a Service that has objects placed in the flow scope when I don't want them to too.
How do I stop local transient variables in closures being put in the flow scope?
Refining the above answer instead of clearing the persistenceContext we simply evict the instances as we finish with them, like so:
Target.list().each {
targets.add(new TargetCommand(name: it.name, id: it.id))
flow.persistenceContext.evict(it)
}
This is still a work-around for not being able to mark the closure variables as transient
The answer to my question is:
the flow object is a map that contains a reference to the "persistenceContext" which is a org.hibernate.impl.SessionImpl so the flow tries to store the entire session, even if the objects are not changed (for context I suppose)
this incorrect example from grails 1.1.x doc gives us a clue what to do:
processPurchaseOrder {
action {
def a = flow.address
def p = flow.person
def pd = flow.paymentDetails
def cartItems = flow.cartItems
flow.clear()
def o = new Order(person:p, shippingAddress:a, paymentDetails:pd)
o.invoiceNumber = new Random().nextInt(9999999) cartItems.each { o.addToItems(it) }
o.save()
[order:o] }
on("error").to "confirmPurchase"
on(Exception).to "confirmPurchase"
on("success").to "displayInvoice"
}
The flow.clear() clears the entire flow map including the persistenceContext or the session, which then makes the whole flow fail due to lack of a session.
so the intermediate "solution" is to use the persistenceContext and in this case clear it. So this works:-
def childFlow = {
start {
action {
sponsorService.updateTargetsFromTaggedContent()
def targets = []
Target.list().each {
targets.add(new TargetCommand(name: it.name, id: it.id))
}
flow.persistenceContext.clear()
[children: targets]
}
on('success').to('selectChild')
on(Exception).to 'finish'
}
The obvious problem with this is that the session is cleared completely, instead of just keeping out things I don't want in the flow.
for want of a better way, here is a generalised solution that removes any non Serializable objects from the persistenceContext of the flow. This could be a service method given the flow:-
def remove = []
flow.persistenceContext.getPersistenceContext().getEntitiesByKey().values().each { entity ->
if(!entity instanceof Serializable){
remove.add(entity)
}
}
remove.each {flow.persistenceContext.evict(it)}
If like me you need to evict all maybe you like to do
flow.persistenceContext.flush()
flow.persistenceContext.persistenceContext.clear()