Sketch of the example
I will start with a basic template, very similar to the one we constructed in this previous tutorial.
This will be the basic structure of our Spring MVC project:
The files MvcConfig.java and SpringMvcAjaxExampleApplication.java are the basic configuration classes of our project. Since the configuration of this example is very simple I will not paste their code in this post, but you have all of it here.
The controllers
We will have two controllers, one for the home page and another for the AJAX requests.
HomeController will just handle the view home.jsp to the user:
/src/main/java/com/hjbello/config/HomeController.java
/src/main/java/com/hjbello/config/HomeController.java
package com.hjbello.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller public class HomeController { private static final Logger logger = LoggerFactory.getLogger(HomeController.class); @RequestMapping(value = "/", method = RequestMethod.GET) public ModelAndView home(Model model) { ModelAndView mv = new ModelAndView(); mv.setViewName("home"); return mv; } }
To handle AJAX request, we will need two POJO's, one for the request objects and another for the response objects:
(I will not include constructors, getters and setters to make this post shorter, but they are necessary)
\src\main\java\com\hjbello\config\AjaxRequest.java
package com.hjbello.config; public class AjaxRequest { private String text1; private String text2; //setters, getters and constructors: [...]
\src\main\java\com\hjbello\config\AjaxResponse.java
package com.hjbello.config; public class AjaxResponse { private String text1; private String text2; private String date; //setters, getters and constructors: [...]
And the ajax controller:
\src\main\java\com\hjbello\config\AjaxController.java
package com.hjbello.config; import java.util.Date; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.fasterxml.jackson.core.JsonProcessingException; @RestController public class AjaxController { @RequestMapping(value = "/ajaxEndpoint", method = RequestMethod.POST) public @ResponseBody ResponseEntity<AjaxResponse> capture (@RequestBody AjaxRequest request) throws JsonProcessingException{ System.out.println("modifying"); AjaxResponse response = new AjaxResponse(); response.setText1(request.getText1() + "MODIFIED"); response.setText2(request.getText2() + "MODIFIED"); Date date = new Date(); response.setDate(date.toString()); return new ResponseEntity<AjaxResponse>(response, HttpStatus.OK); } }
As you see, this very simple controller will just add the string "MODIFIED" to the two text fields in the request and will also give us the date in which it was invoked.
The view, calling the AJAX controller with JQuerry
Finally, let us take a look at the home.jsp view, to see how to invoke the previous RestController using the ajax method of the JQuerry library.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <!DOCTYPE html> <html> <head> <script src="<c:url value="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js" />"></script> <link href="<spring:url value="css/app.css" />" rel="stylesheet" type="text/css"> <title>Welcome</title> </head> <body class="app"> <div> <h1>Welcome!</h1> Click the button to send the form and recieve the response via ajax: </div> <h2>Ajax request:</h2> <form id="callAjax"> <table> <tr> <td>Text 1:</td> <td><input type=text id="text1"></td> </tr> <tr> <td>Text 2:</td> <td><input type=text id="text2"></td> </tr> <tr> <td></td> <td><button type="submit">Send Ajax request</button></td> </tr> </table> </form> <h2>Ajax response:</h2> <table> <tr> <td>Text 1 (response):</td> <td><div id="text1_response"></div></td> </tr> <tr> <td>Text 2 (response):</td> <td><div id="text2_response"></div></td> </tr> <tr> <td>Date (response):</td> <td><div id="date_response"></div></td> </tr> </table> <%-- <input type="hidden" id="csrfToken" value="${_csrf.token}"/> <input type="hidden" id="csrfHeader" value="${_csrf.headerName}"/> --%> <script type="text/javascript"> jQuery(document).ready(function($) { $("#callAjax").submit(function(event) { // Prevent the form from submitting via the browser. event.preventDefault(); callAjax(); }); }); function callAjax() { /* var token = $('#csrfToken').val(); var header = $('#csrfHeader').val(); */ var data = {} data["text1"] = parseInt($("#text1").val()); data["text2"] = parseInt($("#text2").val()); $.ajax({ type : "POST", contentType : "application/json", url : "${home}/ajaxEndpoint/", data : JSON.stringify(data), dataType : 'json', /* beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader(header, token); }, */ timeout : 100000, success : function(data) { console.log("SUCCESS: ", data); display(data); }, error : function(e) { console.log("ERROR: ", e); display(e); }, done : function(e) { console.log("DONE"); } }); } function display (data){ var text1_response = data.text1; var text2_response = data.text2; var date = data.date; $("#text1_response").html(text1_response); $("#text2_response").html(text2_response); $("#date_response").html(date); } </script> </body> </html>
Since I am realizing that the code looks all mixed up, and it is kind of difficult to follow I will summarize it here.
What home.jsp does is the following:
- First, we construct a basic form that we want to send to the controller via AJAX. This form looks like this:
<form id="callAjax"> <table> <tr> <td>Text 1:</td> <td><input type=text id="text1"></td> </tr> <tr> <td>Text 2:</td> <td><input type=text id="text2"></td> </tr> <tr> <td></td> <td><button type="submit">Send Ajax request</button></td> </tr> </table> </form>
- Then in the script tag, we write a listener for onClick event of the button of the form callAjax:
<script type="text/javascript"> jQuery(document).ready(function($) { $("#callAjax").submit(function(event) { // Prevent the form from submitting via the browser. event.preventDefault(); callAjax(); }); }); [...]
- Every time the button is clicked, the fuction callAjax() is invoked. This function is the one that actually calls our controller:
function callAjax() { /* var token = $('#csrfToken').val(); var header = $('#csrfHeader').val(); */ var data = {} data["text1"] = parseInt($("#text1").val()); data["text2"] = parseInt($("#text2").val()); $.ajax({ type : "POST", contentType : "application/json", url : "${home}/ajaxEndpoint/", data : JSON.stringify(data), dataType : 'json', /* beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader(header, token); }, */ timeout : 100000, success : function(data) { console.log("SUCCESS: ", data); display(data); }, error : function(e) { console.log("ERROR: ", e); display(e); }, done : function(e) { console.log("DONE"); } }); }
- If the call is successful the function display(data) is invoked. This function just adds the obtained information inside two tags in our view:
function display (data){ var text1_response = data.text1; var text2_response = data.text2; var date = data.date; $("#text1_response").html(text1_response); $("#text2_response").html(text2_response); $("#date_response").html(date); }
As a last comment, if we were using Spring Security (through a login form for instance) we would need to uncomment the commented lines in home.jsp. That is, we would need to add two hidden forms:
Then we would also need to include the content of these two inputs in the call of the function $.ajax like this
<input type="hidden" id="csrfToken" value="${_csrf.token}"/> <input type="hidden" id="csrfHeader" value="${_csrf.headerName}"/>
Then we would also need to include the content of these two inputs in the call of the function $.ajax like this
var token = $('#csrfToken').val(); var header = $('#csrfHeader').val(); $.ajax({ type : "POST", contentType : "application/json", url : "${home}/ajaxEndpoint/", data : JSON.stringify(data), dataType : 'json', beforeSend: function(xhr) { xhr.setRequestHeader("Accept", "application/json"); xhr.setRequestHeader("Content-Type", "application/json"); xhr.setRequestHeader(header, token); }, timeout : 100000, [...]