Wednesday, January 15, 2014

Submitting JSON via jQuery to a restful Spring web service

In one of my recent posts, "Creating a restful JSON web service which sends and receive objects", I talked about a Spring web service which accepts and returns its response as JSON objects.

The Spring-based controller code looks like this:

@RequestMapping(value="person3", method=RequestMethod.POST)
@ResponseBody
public Person postPerson(@RequestBody Person person) {
personService.save(person);
return person;

}

As you can see, it uses both the @RequestBody and @ResponseBody tags to ensure data is both sent and received as JSON. 

This whole experiment was based on a tutorial I was reviewing, http://codetutr.com/2013/04/09/spring-mvc-easy-rest-based-json-services-with-responsebody/, which demonstrates how to use ajax in the client with restful spring services. However, the @ResponseBody example was only mentioned in the example section, and the client was tested using curl as opposed to a jQuery form.

I was curious about how to fill that gap (post JSON from a jQuery form). The ultimate goal was to submit a form which had JSON sent in the body instead of the url-encoded typical query string - i.e. 

{"name":"e","age":"4"}

and not: 

name=e&age=4

The form returned should reflect the name submitted, just as was done with the regular, non-json-encoded post:


So I experimented a little bit and here's the commented code:

// Save Person AJAX Form Submit
$('#newPersonForm').submit(function(e) {
var $this = $(this)  // convert the form to a regular variable (?) 
var obj = form_to_obj($this) // convert it to a javascript object 
var data = JSON.stringify(obj) // here's the magic;  convert the javacript object to JSON 

// I used a generic ajax post here - it was really to ensure the application type is JSON
// I'm sure there are other ways. But it is a nice way to ensure that a) you're posting in ajax and
// b) the block format is easy to follow

$.ajax({
    type: "POST",
    url: "${pageContext.request.contextPath}/api/person3",
    // The key needs to match your method's input parameter (case-sensitive).
    data: data,
    contentType: "application/json; charset=utf-8",
    dataType: "json",

    // the success function takes the response object, which is a person,
   // and writes it to the page
    success: function(person) {  // the response is returned as a javascript object
$('#personFormResponse').text(person.name + ", age: " + person.age);
},
    failure: function(errMsg) {
        alert(errMsg);
    }
});
e.preventDefault(); // prevent actual form submit and page reload
});
});

// function I grabbed from SO to convert to an object
function form_to_obj (selector) {
  var ary = $(selector).serializeArray();
  var obj = {};
  for (var a = 0; a < ary.length; a++) obj[ary[a].name] = ary[a].value;
  return obj;

}

That's about it. I should note that it was invaluable to use the TCP / IP Monitor to see what was being submitted. It helped me to figure out the following bugs:

1) I was submitting a url-encoded query string instead of JSON. 
2) I was getting 400 bad input error - it turned out I was using a function that didn't have the @RequestBody tag
3) I was getting an invalid media type - I had to set the content to application/JSON

You can see in the TCP/IP monitor output below how the submit is including the JSON object in the body. Also, in the response it receives data as JSON. Since the content type is JSON, its's automatically converted to a JSON object for the browser.  











No comments:

Post a Comment