How to build a Java client to consume a RESTful Grails web service
Question posted on the Grails mailing list:
I'm trying to build a REST client for my REST server (which I build
with the help of the Definitive Guide To Grails) using RESTclient.
I'm using the following code at the server side:
def save = {
def u = new Signup()
u.properties = params
if (u.save()) {
render u as XML
} else {
render u.errors as XML
}
}
and this piece of code at the client:
def signup = new RESTClient(signupsUrl)
def response = signup.post(
contentType: XML,
requestContentType: XML,
body: {
field(name:"surname", "Pascal")
field(name:"infix", "de")
field(name:"lastname", "Vink")
field(name:"email", "pascal@example.com")
field(name:"signupdate", params.signupdate)
}
}
return response.data
Not only does it not put anything in the database, the response.data
is virtually unreadable, at least it's not XML as I would expect.
The following is a snippet of the output of println response.data:
emailSignupfalseorg.springframework.validation.FieldErrornullableSignup.email.nullable.error.Signup.emailSignup.email.nullable.error.emailSignup.email.nullable.error.java.lang.StringSignup.email.nullable.errorsignup.email.nullable.error.Signup.emailsignup.email.nullable.error.emailsignup.email.nullable.error.java.lang.Stringsignup.email.nullable.errorSignup.email.nullable.Signup.emailSignup.email.nullable.emailSignup.email.nullable.java.lang.StringSignup.email.nullablesignup.email.nullable.Signup.emailsignup.email.nullable.emailsignup.email.nullable.java.lang.Stringsignup.email.nullablenullable.Signup.emailnullable.emailnullable.java.lang.StringnullableProperty
[{0}] of class [{1}] cannot be
nullemailSignupinfixSignupfalseorg.springframework.validation.FieldErrornullable
and this goes on for a while (full print here http://gist.github.com/152217)
Does anyone know how to build a compatible client? And perhaps a
server that returns proper XML error messages?
Thanks in advance.
Pascal de Vink
My Answer:
Interestingly I spent a decent portion of this week fighting with similar problems! What I learned is that there are not many examples on the web about how to write more than the simplest restful clients, particularly for POST/PUT. Secondly that there's a difference between sending things as HTTP parameters and as content body. Most examples on the web use params - this is also how information is sent by your browser during an HTML form POST. In this case the content type application/x-www-form-urlencoded should be used. Below I have sent the data as XML inside the request content body, so I used text/xml as the content type. I’m not sure if there is a “best practice” but it seems to me that web services are better aligned to sending XML or JSON in the request content – that way you can post lists of objects or objects within objects.
The client which actually worked in the end, I wrote with Java and the Jersey library which was quite nice. You can do a post like this:
private static final String SERVICE_PREFIX = "http://localhost:8080/myapp/";
String myXml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><machine><model enumType=\"MachineModel\">Forklift</model><name>Big lifter</name></machine>";
Client client = Client.create();
WebResource webResource = client.resource(SERVICE_PREFIX + "machine/saveXml/3");
String response = webResource.type(MediaType.TEXT_XML).accept(MediaType.TEXT_XML).post(String.class, myXml);
System.out.println(response);
Code in my Grails controller to serve the Web service:
def saveXml = {
def xmlMachine = request.XML
def machine = new Machine(name:xmlMachine.name.text(),
model:MachineModel.valueOf(xmlMachine.model.text())).save()
render machine as XML
}
Important things to note
- My example sends xml as content, not as name/value pairs in the params. That’s why I can access the XML with the call request.XML. If you send it as params there’s another handy call, I think request.params or something similar. You can also println(request) in the controller which gives you a bit more idea what’s going on... the value of Content-Length tells you how much content you sent.
- The value of request.XML – in my case xmlMachine – is confusing. If you print it or try to “cast” it as your target object, it doesn’t look like anything. But you can indeed access the xml with “.element.text()”.
- Lastly I’m using an Enum, hence the valueOf syntax. I think there may actually be a more convenient (1 call) way of creating the target object from the parsed XML but I think it might not have worked for me due to my Enum, so I just parse it manually.
If you're still having problems, break it down. Put some XML directly into your controller as a string and try and parse it with the Groovy XML Slurper, and send a response to the client. Once you've got that going, you can remove the hard-coded String and try and actually send the XML from the client.
Add new comment