Yep, you've heard it before but I'm posting it anyway .. I'm New to Groovy and Grails. I'm trying to use best practices to create a Grails Restful Client App (eventually a liferay portlet) which will be able to pull JSON and render it in nice views.
I've found this great script which, with some modification at least pulls objects as JSON from our thirdparty CRM vendor, Awesome! So it's working but, if I'm not mistaken, wouldn't be best to modularize stuff like the OATH parts to a Grails service so it can be used in multiple controllers? I'd like to break the below (functioning) groovy script into the correct parts of the Grails app structure so I have an extensible app following best practices. Due to the version requirements of the portlet plugins, I have to stay with Grails 2.2.5. Any help chopping the code below up and getting into the right controllers, services and views would be greatly appreciated and help me (and others) understand how this works.
Here's the stuff I think might end up in a service
package school.infotech.edu
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseException
import static groovyx.net.http.ContentType.URLENC
import static groovyx.net.http.Method.POST
import static groovyx.net.http.Method.GET
import static groovyx.net.http.ContentType.JSON
#Grapes(
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.6')
)
def sf_login_domain = 'https://crm.thirdpartyvendor.com/'
def instance_domain
def access_token = ''
def consumer_key = 'BIGLONGKEYTHATWORKS'
def consumer_secret = 'SOMENUMBERSTHATWORK'
def auth_username = 'OURACCESSACCOUNTTHATWORKS'
def auth_password = 'WORKINGPASSWORD'
def auth_security_token = 'SECURITYTOKEN'
//Request Access_token and instance domain for work
def http = new HTTPBuilder(sf_login_domain)
def postBody = [
grant_type: 'password',
client_id: consumer_key,
client_secret: consumer_secret,
username: auth_username,
password: auth_password+auth_security_token
]
try{
http.post( path : 'services/oauth2/token',
body : postBody,
requestContentType: URLENC) { resp, json ->
access_token = json.access_token
instance_domain = json.instance_url +"/"
}
}catch(HttpResponseException e){
println "Error code: ${e.statusCode}"
println "Post form: $postBody"
}
println "Access Token $access_token"
println "Instance domain $instance_domain \n"
So I think this stuff might be in a controller or a view but how do I invoke the service stuff?
println "List API Resources \n"
http = new HTTPBuilder(instance_domain)
http.request(GET,JSON) { req ->
uri.path = 'services/data/v28.0/'
headers['Authorization'] = "Bearer $access_token"
response.success = { resp, json ->
assert resp.status == 200
json.each{ key,value ->
println "$key : $value"
}
}
response.failure = { resp, json ->
println resp.status
println json.errorCode
println json.message
}
}
println "List sobject Resources \n"
http.request(GET,JSON) { sobjectreq ->
uri.path = 'services/data/v28.0/sobjects/'
headers['Authorization'] = "Bearer $access_token"
response.success = { resp, json ->
assert resp.status == 200
json.each{ key,value ->
println "$key : $value \n"
}
json.objectDescribe.each{ key,value ->
println "$key : $value"
}
}
}
println "List chatter \n"
http.request(GET,JSON) { req ->
uri.path = 'services/data/v28.0/chatter'
headers['Authorization'] = "Bearer $access_token"
response.success = { resp, json ->
assert resp.status == 200
json.each{ key,value ->
println "$key : $value"
}
}
response.failure = { resp, json ->
println resp.status
println json.errorCode
println json.message
}
}
Outstanding questions are
Which chunk should go where?
How to reference from a service to a controller/
How to display the resulting JSON (as a map?) in a view?
Thanks in advance for any help.
The parts that make the REST call can go into a service that gets injected into a controller. Note I cannot recommend HTTPBuilder as it is a mess of dependencies, you’re better off with http://grails.org/plugin/rest-client-builder or https://github.com/jwagenleitner/groovy-wslite
Related
Hi I'm using Groovy HTTPBuilder to make a POST call similar like this:
http.request( POST, JSON ) { req ->
body = [name:'testName', title:'testTitle']
response.success = { resp, json ->
println resp.statusLine
println json
}
}
However due to a Bug (which I cannot solve myself), the REST server returns a JSON that is not correctly formatted resulting in the following exception when my application is trying to parse it:
groovy.json.JsonException: Unable to determine the current character, it is not a string, number, array, or object
I'm fairly new to groovy closures and HTTPBuilder, but is there a way to make the application check if the JSON is actually valid before parsing it and returning null if so?
I'm not sure this is a good idea, but it is possible to supply your own JSON parser, which facilitates the original request. It also affords the opportunity to "fix" the JSON if the known bug can be fixed.
Example below. The parser is essentially the same code that HTTPBuilder uses, with the exception of a hard-coded charset of UTF-8.
#Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7' )
import groovy.json.*
import groovyx.net.http.*
import static groovyx.net.http.Method.*
import static groovyx.net.http.ContentType.*
def url = "http://localhost:5150/foobar/bogus.json"
def http = new HTTPBuilder(url)
http.parser."application/json" = { resp ->
println "tracer: custom parser"
def text = new InputStreamReader( resp.getEntity().getContent(), "UTF-8");
def result = null
try {
result = new JsonSlurper().parse(text)
} catch (Exception ex) {
// one could potentially try to "fix" the JSON here if
// there is a known bug in the server
println "warn: custom parser caught exception"
}
return result
}
http.request( POST, JSON ) { req ->
body = [name:'testName', title:'testTitle']
response.success = { resp, json ->
println resp.statusLine
println json
}
}
I have the following Groovy script (not a Grails app) that is returning a JSON-like, but it is not strictly valid JSON.
String baseURL = 'https://test.com'
File userFile = new File("./user.json")
def client = new HTTPBuilder(baseUrl)
client.headers['Content-Type'] = 'application/json'
client.request(GET, JSON) { req ->
requestContentType = JSON
headers.Accept = 'application/json'
response.success = { resp, json ->
userFile.append json.toString()
println JsonOutput.toJson(json.toString())
}
}
I am trying to create a JSON output file. I have tried using JsonOutput.prettyPrint and I looked at JsonBuilder, but that looks like I would have to build the JSON structure manually when Groovy should support the output. This is what I am getting back.
{AssetNumber=AssetNumber1, DeviceFriendlyName=FriendlyName1, PhoneNumber=17035551231, SerialNumber=SerialNumber1, Udid=Udid1, UserEmailAddress=user1#email.com, UserId=userId1, UserName=userName1}
As I said, this is JSON-like, but not strictly valid. I was expecting something like:
{"AssetNumber": "AssetNumber1", "DeviceFriendlyName": "FriendlyName1"....}
Any ideas?
It works perfectly fine (groovy v 2.3.6):
import groovy.json.*
def pretty = JsonOutput.prettyPrint(JsonOutput.toJson([1:2]))
assert pretty == """{
"1": 2
}"""
In this closure:
response.success = { resp, json ->
userFile.append json.toString()
println JsonOutput.toJson(json.toString())
}
You're getting an instance of Map under json variable. You do not need to turn it into a string. Instead use:
userFile.append JsonOutput.toJson(json)
println JsonOutput.toJson(json)
I have a service that I am built\using returning data in the below format.
def responseData = [
'results': results,
'status': results ? "OK" : "Nothing present"
]
render(responseData as JSON)
The output looks like this, I have verified the output according to Fiddler
{"results":[{"class":"com.companyName.srm.ods.territory.Apo","id":2,"apoId":"5T9B0"}],"status":"OK"}
This is a simple POST call with a body of parameters from a search.
Using HTTPBuilder I get a different result
http.request(groovyx.net.http.Method.POST, groovyx.net.http.ContentType.URLENC) {req ->
uri.path = restUrl
body = requestData
response.success = {resp, json ->
println resp.statusLine.statusCode
println resp.statusLine
def slurper = new JsonSlurper()
String s = json.toString()
println s
returnJson = slurper.parseText(s)
}
response."422" = {resp, json ->
println ${resp.statusLine}
}
response.failure = {resp ->
println ${resp.statusLine}
}
}
["results":[{"class":"com.companyName.srm.ods.territory.Apo","id":2,"apoId":"5T9B0"}],"status":"OK":null]
This turns into a Mapped pair where the key is the JSON and the value is null, which is confusing as to why the HTTPBuilder is doing that.
In order to parse to JSON, I have to the following additional coding
s = s.replace(':null]', '')
s = s.replace('[', '')
This seems overly complicated for this type of implementation.
I have turned debug and nothing interesting is coming from that.
Any ideas
I use builder 0.7.1 and get json response the next way:
http.request(Method.POST, ContentType.TEXT) {
uri.path = pathToService
headers.'User' = user
headers.Accept = 'application/json'
body = requestBody //here I post some json
response.success = { resp, reader ->
//println reader.text;
println "response status: ${resp.statusLine}"
return = reader.text
}
response.failure = { resp, reader ->
println "Request failed with status ${resp.status}"
reader.responseData.results.each {
println " ${it.titleNoFormatting} : ${it.visibleUrl}"
}
}
}
I read this and several other postings on SO and elsewhere about how to send a Post call via HttpBuilder with JSON as the data content. My problem is that NONE OF THOSE SOLUTIONS are working!
My problem is only slightly different. I have existing JSON data in a file. When I attempt to send this to the REST interface with curl:
curl -X POST -u "username:password" -d #/path/to/myFile.json http://localhost:8080/path/here --header "Content-Type:application/json"
all works perfectly well. Here is where I am at (some extra code IS in there, read on):
def myFile = new File('/path/to/myFile.json')
if (!myFile.exists()) println "ERROR! Do not have JSON file!"
def convertedText = myFile.text.replaceAll('\\{', '[')
convertedText = convertedText.replaceAll('\\}', ']')
def jsonBldr = new JsonBuilder()
jsonBldr myFile.text
println jsonBldr.toString()
def myClient = new groovyx.net.http.HTTPBuilder('http://username:password#localhost:8080/my/path')
myClient.setHeaders(Accept: 'application/json')
results = myClient.request(POST, JSON) { req ->
body = [ jsonBldr.toString() ]
requestContentType = JSON
response.success = { resp, reader ->
println "SUCCESS! ${resp.statusLine}"
}
response.failure = { resp ->
println "FAILURE! ${resp.properties}"
}
}
This results in the 'failure' closure with this data:
statusLine:HTTP/1.1 400 Exception evaluating property 'id' for java.util.ArrayList, Reason: groovy.lang.MissingPropertyException: No such property: id for class: java.lang.String
FWIW, there is no "id" in my JSON anywhere. If I change the "body" line from "[ jsonBldr.toString() ]" to "[ convertedText ]" - which is why that code is up there, I get the same error. If I take out the brackets on the body, I get an error stating that the body is not data for an array (as its a Map).
Can anyone (far groovier than I) tell me what the %%$## I am doing wrong???
You need JsonSlurper instead of JsonBuilder. After which the implementation would look like:
def myFile = new File('/path/to/myFile.json')
if (!myFile.exists()) println "ERROR! Do not have JSON file!"
def bodyMap = new JsonSlurper().parseText(myFile.text)
def myClient = new groovyx.net.http.HTTPBuilder('http://username:password#localhost:8080/my/path')
modelClient.setHeaders(Accept: 'application/json')
results = myClient.request(POST, JSON) { req ->
requestContentType = JSON
body = bodyMap
response.success = { resp, reader ->
println "SUCCESS! ${resp.statusLine}"
}
response.failure = { resp ->
println "FAILURE! ${resp.properties}"
}
}
However, I am not clear what is difference between myFile and modelFile in your code.
I've found this doc on how to post JSON data using HttpBuilder. I'm new to this, but it is very straightforward example and easy to follow. Here is the code, assuming I had imported all required dependencies.
def http = new HTTPBuilder( 'http://example.com/handler.php' )
http.request( POST, JSON ) { req ->
body = [name:'bob', title:'construction worker']
response.success = { resp, json ->
// response handling here
}
}
Now my problem is, I'm getting an exception of
java.lang.NullPointerException
at groovyx.net.http.HTTPBuilder$RequestConfigDelegate.setBody(HTTPBuilder.java:1131)
Did I miss something? I'll greatly appreciate any help you can do.
I took a look at HttpBuilder.java:1131, and I'm guessing that the content type encoder that it retrieves in that method is null.
Most of the POST examples here set the requestContentType property in the builder, which is what it looks like the code is using to get that encoder. Try setting it like this:
import groovyx.net.http.ContentType
http.request(POST) {
uri.path = 'http://example.com/handler.php'
body = [name: 'bob', title: 'construction worker']
requestContentType = ContentType.JSON
response.success = { resp ->
println "Success! ${resp.status}"
}
response.failure = { resp ->
println "Request failed with status ${resp.status}"
}
}
I had the same problem a while ago and found a blog that noted the 'requestContentType' should be set before 'body'. Since then, I've added the comment 'Set ConentType before body or risk null pointer' in each of my httpBuilder methods.
Here's the change I would suggest for your code:
import groovyx.net.http.ContentType
http.request(POST) {
uri.path = 'http://example.com/handler.php'
// Note: Set ConentType before body or risk null pointer.
requestContentType = ContentType.JSON
body = [name: 'bob', title: 'construction worker']
response.success = { resp ->
println "Success! ${resp.status}"
}
response.failure = { resp ->
println "Request failed with status ${resp.status}"
}
}
Cheers!
If you need to execute a POST with contentType JSON and pass a complex json data, try to convert your body manually:
def attributes = [a:[b:[c:[]]], d:[]] //Complex structure
def http = new HTTPBuilder("your-url")
http.auth.basic('user', 'pass') // Optional
http.request (POST, ContentType.JSON) { req ->
uri.path = path
body = (attributes as JSON).toString()
response.success = { resp, json -> }
response.failure = { resp, json -> }
}
I found an answer in this post: POST with HTTPBuilder -> NullPointerException?
It's not the accepted answer, but it worked for me. You may need to set the content type before you specify the 'body' attribute. It seems silly to me, but there it is. You could also use the 'send contentType, [attrs]' syntax, but I found it more difficult to unit test. Hope this helps (late as it is)!
I gave up on HTTPBuilder in my Grails application (for POST at least) and used the sendHttps method offered here.
(Bear in mind that if you are using straight Groovy outside of a Grails app, the techniques for de/encoding the JSON will be different to those below)
Just replace the content-type with application/json in the following lines of sendHttps()
httpPost.setHeader("Content-Type", "text/xml")
...
reqEntity.setContentType("text/xml")
You will also be responsible for marshalling your JSON data
import grails.converters.*
def uploadContact(Contact contact){
def packet = [
person : [
first_name: contact.firstName,
last_name: contact.lastName,
email: contact.email,
company_name: contact.company
]
] as JSON //encode as JSON
def response = sendHttps(SOME_URL, packet.toString())
def json = JSON.parse(response) //decode response
// do something with json
}