I am doing a POC for Command object in Grails Controller and i face a road block where the binding doesn't happen.
Command Object
public class Employee {
String name;
int age;
#Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Controller :
class EmployeeController {
def index() {
render (" Employee Index Called ");
}
def emp(Employee employee){
println( employee.toString());
render(" Success");
}
}
I am calling emp action from a rest client as below
Method: POST
URL : http://localhost:8080/Grails/employee/emp
Request Body : { "name": "Vinod", "age": 34 }
I get employee.name as null and employee.age=0 always
I am suspecting my Request Body is wrong but not sure. Please help where I am going wrong.
Thanks
As far as I know, json is not bound automatically to command object (defined as action method arg), only plain params are. To get a hold on JSON request you have to call
def json = request.JSON
and then you can try binding it to your object:
def co = new MyCommandObject( json )
For POST request method, I had to set 'Content-Type' header as 'application/json'
Grails constructs command object from JSON in request body of POST(JSON to Java Object)
Whenever Grails finds 'Content-Type' header as 'application/json' it will automatically parse the request body in JSON to command object and pass it on to the controller method.
You may also want to make the command class validatable by doing the following.
In Grails 3:
Your command class can to implement grails.validation.Validateable trait.
import grails.validation.Validateable
class Employee implements Validateable{
String name
int age
}
In Grails 2.x:
You command class can have grails.validation.Validateable annotation
#grails.validation.Validateable
class Employee implements Validateable{
String name
int age
}
Related
Is there any support for using abstract command objects in controller action parameters? Then depending on the given parameters in a JSON request it would select the correct command object?
For example something like:
class SomeController {
def someAction(BaseCommand cmd){
// cmd could be instance of ChildCommandOne or ChildCommandTwo
}
class BaseCommand {
String paramOne
}
class ChildCommandOne extends BaseCommand {
String paramTwo
}
class ChildCommandTwo extends BaseCommand {
String paramThree
}
}
As of now I've been using request.JSON to detect the passed in parameters and instantiate the correct Command object. Is that my only option to handle this sort of case?
EDIT :
To clarify the use case here. I have two domain models that share the same base class domain model and I'm modeling the inheritance in the database using the default table-per-hierarchy model.
In my case, one of the child domain models Model A requires a non-nullable String called body, that is a Text entry, while the other Model B requires a non-nullable String called directUrl. These represent announcements that can be made on the platform. Model A being a write in entry that contains the announcement body while Model B represents a link to a third party site that contains the actual announcement.
In these sort of scenarios I've traditionally put an if statement in the controller action that determines which related command object to instantiate but I am hoping for a cleaner method.
It won't work this way. Grails needs a concrete class (with default public constructor) to bind request params to a command object instance. Therefore this class is to be defined explicitely as action's argument.
I guess you will have to call binding manually depending on what map contains.
See RootModel.from(Map map). In your case Map would be params from Controller
import static com.google.common.base.Preconditions.checkNotNull
import spock.lang.Specification
import spock.lang.Unroll
class CommandHierarchySpec extends Specification {
#Unroll
def "should create object of type #type for map: #map"() {
when:
def modelObj = RootModel.from(map)
then:
modelObj.class == type
where:
type | map
ModelA | [body: 'someBody', test: 'test']
ModelB | [directUrl: 'directUrl', test: 'test']
}
def "should throw ISE when map does not contain neither body nor url"() {
when:
RootModel.from(a: 'b')
then:
thrown(IllegalStateException)
}
}
abstract class RootModel {
static RootModel from(Map map) {
checkNotNull(map, "Parameter map mustn't be null")
RootModel rootModel
if (map.body) {
rootModel = new ModelA()
} else if (map.directUrl) {
rootModel = new ModelB()
} else {
throw new IllegalStateException("Cannot determine command type for map: $map")
}
map.findAll { key, value -> rootModel.hasProperty(key) }
.each {
rootModel.setProperty(it.key, it.value)
}
rootModel
}
}
class ModelA extends RootModel {
String body
}
class ModelB extends RootModel {
String directUrl
}
I'm using MongoDB exclusively with a Grails REST app and the domain shown below. This respond call fails:
#Secured(['permitAll'])
def index() {
respond Person.list()
}
with the error
ERROR errors.GrailsExceptionResolver - IllegalAccessException occurred when processing request: [GET] /teesheet/person
Class org.codehaus.groovy.grails.web.converters.marshaller.json.GroovyBeanMarshaller can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public". Stacktrace follows:
Message: Class org.codehaus.groovy.grails.web.converters.marshaller.json.GroovyBeanMarshaller can not access a member of class java.util.Collections$UnmodifiableCollection with modifiers "public"
Attempting to convert the collection to JSON also fails with the same error.
def personList = Person.list() as JSON
The low level API works.
package com.tworks.teesheet
import grails.rest.Resource
class Person {
String name
String firstName
String lastName
String email
User userPerson
TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles")
Date dateCreated = new Date()
Date dateModified = new Date()
}
Assuming you're using Mongo for Grails plugin, you need #Entity for domain classes...
import grails.persistence.Entity
#Entity
class Person {
static mapWith = "mongo"
String name
String firstName
String lastName
String email
}
I added mapWith="mongo" since I wasn't sure if you're using the hibernate plugin alongside the mongo plugin; if you're not, remove hibernate, otherwise, it may interfere.
I'm currently using the low level calls to iterate using the cursor but it seems like the respond Person.list() should work. This code is working:
def cursor = Person.collection.find()
def items = []
try {
while (cursor.hasNext()) {
items << com.mongodb.util.JSON.serialize(cursor.next())
}
} finally {
cursor.close()
}
log.info("items: ${items}")
render items
I'm trying to use the Grails shopping cart plugin found here: http://grails.org/plugin/shopping-cart
I was able to successfully install the plugin in my application, as well as inject the service in my Controller:
class TestController {
def shoppingCartService
def index() {
def s = new DomainObj(name: "A Plain Ole Domain Object")
s.addToShoppingCart()
}
}
This appears to be adding the product to my shopping cart, as I expected. However, the problem I'm encountering now is actually listing the items out from the cart. According to the debugger, after running the above code, the shopping cart does indeed have an item (s) in it, as it returns:
com.metasieve.shoppingcart.ShoppingItem : 1
The item is properly being added to the cart, but now I would like to actually list the name of the item out again, so in this case, I want to display the name A Plain Ole Domain Object. How do I do this?
I'm unsure of the syntax for getting the actual objects back from the cart. The documentation doesn't describe how to do this clearly, as it merely states that the following should work:
def checkedOutItems = shoppingCartService.checkOut()
checkedOutItems.each {
println it['item']
println it['qty']
}
But that outputs
com.metasieve.shoppingcart.ShoppingItem : 1 , which is only a reference to some arbitrary item in the cart. I want to get back the actual name of my item.
Thanks in advance.
EDIT:
My domain class (DomainObj) is defined as follows:
class DomainObj extends com.metasieve.shoppingcart.Shoppable {
String name
static constraints = {
name blank: false
}
}
EDIT #2:
def index() {
def s = new DomainObj(name: "A Plain Ole Domain Object")
s.addToShoppingCart()
def r = new DomainObj(name: "Second Plain Ole Domain Object")
r.addToShoppingCart()
def checkedOutItems = shoppingCartService.checkOut()
println currentItems
println "-----"
checkedOutItems.each {
println it['item']
println it['qty']
}
}
The output of this is:
[com.metasieve.shoppingcart.ShoppingItem : 1, com.metasieve.shoppingcart.ShoppingItem : 2]
com.metasieve.shoppingcart.ShoppingItem : 2
1
com.metasieve.shoppingcart.ShoppingItem : 1
1
According to the documentation it["item"] gives you back the entity of a domain class that extends Shoppable. So in this case when you are printing it out it's calling the toString() method of that domain class. If you want that to return the value of the name property you need to implement your own toString(). Here is such an example
#Override
String toString() {
return name
}
EDIT:
Well as it's not clear from the documentation it['item'] is a pointer to the Shoppable instance which you can then use to query for the actual product in your cart like this:
com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
Thus the following will print out the toString() value of your products
checkedOutItems.each {
println com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
println it['qty']
}
For testing I created the following domain and controller.
Domain:
package com.test
class MyProduct extends com.metasieve.shoppingcart.Shoppable {
String name
static constraints = {
name(blank: false)
}
#Override
String toString() {
return name
}
}
Controller:
package com.test
class MyProductController {
def shoppingCartService
def index() {
def p1 = new MyProduct(name: 'one')
p1.save(flush: true, failOnError: true)
p1.addToShoppingCart()
def p2 = new MyProduct(name: 'two')
p2.save(flush: true, failOnError: true)
p2.addToShoppingCart()
def checkedOutItems = shoppingCartService.checkOut()
checkedOutItems.each {
println com.metasieve.shoppingcart.Shoppable.findByShoppingItem(it['item'])
println it['qty']
}
}
}
anyone know how to send JSON in ActionScript 2?
I tried using XML() but it changes all " into " before sending it. Ex:
var callout = new XML({ "name": "John"})
callout.sendAndLoad('http://api.app.com', new XML());
but what gets sent is { "name": "John"}
also cannot upgrade to AS3 (wish i could)
Here's how you do it
Create a new class that extends LoadVars and override the "toString" method to return the JSON. You can then use load() and sendAndLoad() to send JSON to an URL
Example:
class SendJson extends LoadVars {
public var data:String; // Put your JSON here
public function toString() {
return data;
}
}
I have textfield for birthDate. When a user enter invalid date, let say for example a String, error message successfully displayed as fielderror. But in my console, I got this error:
java.lang.NoSuchMethodException: Profile.setBirthDate([Ljava.lang.String;)
Have I missed something that's why I encountered the error?
In your Action class you dont have a method called setBirthDate(String birthDate), add it your issue should be resolved.
Note check to see that you have placed all getter and setter in your action class for all properties.
I think in your JSP you have :
<s:textfield name="birthDate" />
Struts will try to map this to setBirthDate(String string), since this method is missing in your action hence the NoSuchMethodException
Update:
To convert String to Date:
public class MyStringToDateConverter extends StrutsTypeConverter {
public Object convertFromString(Map context, String[] values, Class toClass) {
//Parse String to get a date object
}
public String convertToString(Map context, Object o) {
// Get the string from object o
}
}
If you are using Annotation in your action class then add #Conversion() to your action
#Conversion()
public class MyAction extends ActionSupport{
public Date myDate = null;
#TypeConversion(converter="MyStringToDateConverter") //Fully qualified name so if this class is in mypackage then converter will be "myPackage.MyStringToDateConverter"
public void setMyDate(Date date) {
this.myDate = date;
}
}
If you dont want to use Annotation then you can look at the official documentation for example.