Spring Hibernate JPA JSON API with JQuery AJAX

Continuing my series of posts on cleanly decoupling the CMS solution from the content-rendering applications, I’m now converting the web application from my previous post to become a fully-formed JSON API. First off we’ll remove the methods to retrieve the model as a POJO and instead only return JSON objects. Then we’ll rewrite the front-end to use JQuery to manipulate the data-layer using our updated API in an AJAX manner. The reasons for doing this are two-fold. One; as I pointed out in my comment to the last post, JSP forms do not allow sending DELETE via the HTTP protocol (only GET and POST). Secondly, it’s a more modern approach and brings us half-way to our ultimate goal. Along the way, I’ll clean up the rendering web application so we’ll have all content displayed and edited on a single page, correctly leveraging the power of the AJAX approach.

Firstly, since we’re going to use AJAX we don’t need more than a single view, “/articles”. Note the @autowired annotated ArticleRepository remains our link to the Article data object.

	@Controller
	public class CMSController {
		@Autowired
		private ArticleRepository repository;		
	
		private static final Logger logger = LoggerFactory.getLogger(CMSController.class);
	
		/*********************** DISPLAY ****************************/
		@RequestMapping(value = "/articles", method = RequestMethod.GET)
		public String viewAllArticles(Locale locale, Model model) {
			logger.info("/articles called - The client locale is {}.", locale);
			return "articles";
		}


Next, we replace the previous read methods with the JSON equivalents. As stated last time;

When Spring sees the following;

  1. Jackson library exists in the project classpath
  2. annotation-driven is enabled in config
  3. Return method annotated with @ResponseBody

Spring will handle the JSON conversion automatically. This also means that calling saveArticle with a supplied JSON object is automatically converted to an Article POJO!

	/********************** RETRIEVE & SAVE ************************/
	
	@RequestMapping(value ="/allArticles", method = RequestMethod.GET)
	public @ResponseBody
	Iterable
getArticles() { logger.info(“/allArticles called – returning all articles”); return repository.findAll(); } @RequestMapping(value=”/saveArticle”, method=RequestMethod.POST) @ResponseBody public Article saveArticle(@RequestBody Article article) { logger.info(“/saveArticle called – saveArticle article with ID (nullmeans a new article) : “+article.getId()); Article created = repository.save(article); return created; } @RequestMapping(value =”/viewArticle/{id}”, method = RequestMethod.GET) public @ResponseBody Article getArticle(@PathVariable Integer id) { return repository.findOne(id); } @RequestMapping(value =”/deleteArticle/{id}”, method = RequestMethod.DELETE) public @ResponseBody void deleteArticle(@PathVariable Integer id) { repository.delete(id); }

Incidentally, a fundamental aspect of writing unit tests is that the implementation code being tested should be written to satisfy the tests being written for them but implementation code should not be written purely to provide for the tests. Using @autowired presents a problem here as there are no setters written. Enter Mockito. The @InjectMocks annotation adds our mocked ArticleRepository to the CMSController class being tested.

	@RunWith(MockitoJUnitRunner.class)
	public class CMSControllerTest {
	
	@Mock
	ArticleRepository repository;
	
	@InjectMocks 
	CMSController cmsController = new CMSController();
	
	Article articleOne = new Article();

	@Before
	public void setUp() throws Exception {		
		MockitoAnnotations.initMocks(this);		
	}

We have a single view now – http://localhost:8080/publisher/articles.

Screenshot from 2013-12-18 17:45:58

 

We can add a new article or edit and delete an existing one all within the same page.Screenshot from 2013-12-18 17:47:04

I use the JQuery $(document).ready(function() function to populate the “articlefeed” DIV


 

in my articles.jsp. In the myjs.js file, this DIV is populated with the list of JSON objects (articles) retrieved.

$(document).ready(function(){		
	$('#submit-article').click(function()
	{ 
		var myid= $('#article-id').val();				
		var article = {
		 	id: myid,
		 	headline: $('#article-headline').val(),
		 	previewText:$('textarea[name=article-previewtext]').val(),
		 	fullText:$('textarea[name=article-text]').val()
	};

		var the_url = '/publisher/saveArticle';
		var xhr = new XMLHttpRequest();
		xhr.open("POST", the_url);
		xhr.setRequestHeader('Content-Type', 'application/json');
		xhr.onreadystatechange = function () {
	    if (xhr.readyState == 4 && xhr.status == 200) {
		// alert(xhr.responseText);
		location.reload();		
	    }
	}
	xhr.send(JSON.stringify(article));						  
	});

	var xhr = new XMLHttpRequest();
	xhr.open("GET", "/publisher/allArticles", true);
	xhr.onreadystatechange = function() {
	if (xhr.readyState == 4) {
	  var data = JSON.parse(xhr.responseText);
	  $.each(data, function() {      		
		var theHeadlinehtml = '

‘+(this.headline)+’

‘; var theLinkhtml = ‘

‘+this.previewText+’

‘; $(‘#articlefeed’).append(theHeadlinehtml); $(‘#articlefeed’).append(theLinkhtml); var theButtonhtml = ‘

‘; $(‘#articlefeed’).append(theButtonhtml); }); } }; xhr.send(); });

You’ll notice that I am using javascript XMLHttpRequest to get JSON data, even though I could use the JQuery call, $.getJSON. It really doesn’t matter as you can use any of the many javascript libraries calls to fetch JSON from a RESTful API. My reason for sticking with XMLHttpRequest was simply to side-step problems with the same origin policy for my browser (Chromium) which prevented the JQuery equivalent.

And that’s all there is to converting the app to a fully-JSONified web service. The code is on GITHUB here but check back soon where I’ll use this webservice to cleanly keep the editing of the data separated from the rendering. I’ve already done it here but I’ll go further and show a simple method to enable us to move CMS or use more than one.

 

Bookmark the permalink.

Leave a Reply