Skip to main content

Spring MVC - Ajax based form processing using JQuery and JSON with server side validation

Spring MVC provides support for processing the form as well as server side validation. It maps request parameters to form backing bean and validate the bean object if we have used @Valid annotation. When we submit the form, form get displayed with the error messages if validation is failed. Error messages are managed by Spring MVC and spring MVC binds them to the input fields.

But, If we want to submit the form using ajax request, form page will not refresh and spring MVC cannot send validation error messages to browsers. In this case, server side validation does not work as per my expectations. So here I devised my approach to use the spring MVC validation even in ajax based form submission. 

I am using JQuery to serialize the form data and capturing them in controller. In controller, form data is being mapped in User bean and User bean is validated based on field validation annotations. Now if validation is get failed and some errors are appeared there, I collect them in UserJsonResponse object. This is the object being sent to browser in JSON format. I am putting errors, status and bean object in this object to send the errors to browser. Spring MVC provides the facility @ResponseBody that converts the returning object in JSON output.

Now I am processing this output in webpage using the javascript where, all messages are being extracted and associated to corresponding  input fields.

Controller

package controller;

import java.beans.PropertyEditorSupport;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import bo.User;

@Controller
public class RegistrationController {
    @Autowired
    private MessageSource messages;
    
    @ModelAttribute
    public User exposeUser(){
        System.out.println("creating @ModelAttribute object");
        return new User();
    }
    
    @RequestMapping(value = "/submituser",method=RequestMethod.POST )
    public  @ResponseBody UserJsonResponse submitUser(@Valid User user,BindingResult bindingResult){
        System.out.println("Submited User Data : \n"+user);
        UserJsonResponse userJsonResponse=new UserJsonResponse();
        if(bindingResult.hasErrors()){
            Map<String ,String> errors=new HashMap<StringString>();
            List<FieldError> fieldErrors = bindingResult.getFieldErrors();
            for (FieldError fieldError : fieldErrors) {
                String[] resolveMessageCodes = bindingResult.resolveMessageCodes(fieldError.getCode());
                String string = resolveMessageCodes[0];
                //System.out.println("resolveMessageCodes : "+string);
                String message = messages.getMessage(string+"."+fieldError.getField()new Object[]{fieldError.getRejectedValue()}null);
                //System.out.println("Meassage : "+message);
                errors.put(fieldError.getField(), message)    ;
            }
            userJsonResponse.setErrorsMap(errors);
            userJsonResponse.setUser(user);
            userJsonResponse.setStatus("ERROR");
        }else{
            userJsonResponse.setStatus("SUCCESS");
        }
        return userJsonResponse;
    }
    
    public MessageSource getMessages() {
        return messages;
    }
    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }
    ////////////////////////////////////////////////////////////
    class UserJsonResponse{
        private String status;
        private Map<String,String> errorsMap;
        private User user;
        public String getStatus() {
            return status;
        }
        public void setStatus(String status) {
            this.status = status;
        }
        public Map<String,String> getErrorsMap() {
            return errorsMap;
        }
        public void setErrorsMap(Map<String,String> errorsMap) {
            this.errorsMap = errorsMap;
        }
        public User getUser() {
            return user;
        }
        public void setUser(User user) {
            this.user = user;
        }
    }
    
    
     @InitBinder
     protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
         binder.registerCustomEditor(long.classnew PropertyEditorSupport() {
         @Override
         public void setAsText(String text) {
             if(text.trim().length()==0){
                 text="0";
             }
             long ch = Long.parseLong(text);
             setValue(ch);
         }
         });
        
     }
}


User Bean
package bo;

import javax.validation.Valid;
import javax.validation.constraints.Digits;
import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.Email;
import org.hibernate.validator.constraints.NotEmpty;

public class User {

    private int id;
    @Size(min = 6, max = 10)
    @NotEmpty
    String name;
    @Email
    String email;

    long phone;
    @Valid
    private Address address;

    public User() {

    }

    public User(String name, String email, long phone) {

        this.name = name;
        this.email = email;
        this.phone = phone;
    }


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public long getPhone() {
        return phone;
    }

    public void setPhone(long phone) {
        this.phone = phone;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "User [id=" + id + ", name=" + name + ", email=" + email
                + ", phone=" + phone + "]";
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

}


Address
package bo;

import javax.validation.constraints.Size;

import org.hibernate.validator.constraints.NotEmpty;

public class Address {

    @NotEmpty
    String addressLine1;
    String addressLine2;
    
    @NotEmpty
    String city;
    long pincode;

    public String getAddressLine1() {
        return addressLine1;
    }

    public void setAddressLine1(String addressLine1) {
        this.addressLine1 = addressLine1;
    }

    public String getAddressLine2() {
        return addressLine2;
    }

    public void setAddressLine2(String addressLine2) {
        this.addressLine2 = addressLine2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public long getPincode() {
        return pincode;
    }

    public void setPincode(long pincode) {
        this.pincode = pincode;
    }

    @Override
    public String toString() {
        return "Address [addressLine1=" + addressLine1 + ", addressLine2="
                + addressLine2 + ", city=" + city + ", pincode=" + pincode
                + "]";
    }

}

messages.properties
Size.user.name = Name must be of 4 to 10 character NotEmpty.user.name = Name is required NotEmpty.user.address.addressLine1= addressLine1 is required NotEmpty.user.address.city = city is required Email.user.email=Email {0} you entered is invalid NotEmpty.user.email = Email is required typeMismatch.user.phone = This is not a number! typeMismatch.user.address.pincode = This is not a number!

Form.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript">
    jQuery(document).ready(function(){
        jQuery("#userform").submit(function(e){
            jQuery(".formFieldError").remove();
            jQuery.ajax({
                    url: jQuery(this).attr("action"),
                    context: document.body,
                    type: 'post',
                    data:jQuery(this).serialize()
                }).done(function(res) {
                    if(res.status==="ERROR"){
                        for(var key in res.errorsMap){
                            var err="<span class=\"formFieldError\" id=\""+key+"Id\">"+res.errorsMap[key]+"</span>";
                            jQuery("[name^='"+key+"']").after(err);
                        }
                    }else{                      
                        jQuery("#msg").html("Form submitted");
                    }
                }).fail(function(data){
                    jQuery("#msg").html("<span class=\"formFieldError\">Server failed to process request</span>");
                });
            return false;
        });
    });
</script>
<style type="text/css">
.formFieldError{
color:red;
}
</style>
</head>
    <body>
    
        <form id="userform" action="submituser" method="post"  >
            Name  :<input type="text" name="name"/><br/>
            Email  :<input type="text" name="email"/><br/>
            Phone  :<input type="text" name="phone"/><br/>
            AddressLine1  :<input type="text" name="address.addressLine1"/><br/>
            AddressLine2  :<input type="text" name="address.addressLine2"/><br/>
            City  :<input type="text" name="address.city"/><br/>
            Pincode  :<input type="text" name="address.pincode"/><br/>
            <input type="submit"/>
        </form>
        <div id="msg"></div>
    
    </body>
</html>

Dispatcher-Servlet-Config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/mvc  
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="controller"></context:component-scan>
    <mvc:annotation-driven />
    <mvc:resources mapping="/**" location="/" />
    

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name = "prefix" value="/WEB-INF/jsps/" />
        <property name = "suffix" value=".jsp" />
    </bean>
    <bean class="org.springframework.context.support.ResourceBundleMessageSource"
        id="messageSource">
        <property name="basename" value="messages" />
    </bean>
</beans>

Download Source Code 
Spring framework

Comments

Anonymous said…
Good example! That is what I want. But I have a question: this form submiting calling reload page or not?
Hemraj said…
Its ajax based form submission. Page will no be reloaded.
Anonymous said…
please post for download link for this example
Anonymous said…
Please post download link
Hemraj said…
Added link to download the source code.
Anonymous said…
I have an error

Request processing failed; nested exception is org.springframework.context.NoSuchMessageException: No message found under code 'NotEmpty.user.address.addressLine1' for locale 'null'.

can you help it? :(

Popular posts from this blog

Using HyperSQL (HSQLDB)

HSQLDB is a portable RDBMS implemented in pure java. It can be embedded with your application as well as can be used separately. It is very a small database that supports almost all features of the standard database system. It comes with small jar file that can be found in lib folder. The HSQLDB jar package is located in the /lib directory of the ZIP package and contains several components and programs. Core components of jar file are : HyperSQL RDBMS Engine (HSQLDB), HyperSQL JDBC Driver, Database Manager, and Sql Tool. Installing and Using Download: download latest release of HyperSQL database from http://hsqldb.org website and extract it. You will see following contents. Here "bin" directory contains some batch files those can be used to run a swing based GUI tool. You can use runManagerSwing.bat to connect to database, but database must be on before running it. Directory lib contains File hsqldb.jar . It is the database to be used by you. Running database First

How to handle values from dynamically generated elements in web page using struts2

Some time you will see the form containing the button " Add More " . This facility is provided for the user to get the values for unknown number of repeating for some information. for example when you are asking to get the projects details from user, you need to put the option to add the more project for the user since you don't known how many projects user have. In the HTML form, you repeat the particular section to get the multiple values for those elements. In Html page , you can put the option to add new row of elements or text fields by writing the java script or using JQuery API. Now, the question is that how to capture the values of dynamically generated text fields on the server. Using the servlet programming you can get the values by using getParameters() method that resultants the array of the parameter having the same name. But this limit you to naming the text fields in the HTML form. To ally this approach, you have to take the same name for t

In Process Mode of HSQLDB in web application.

If you want to use the database into your web application, you can use the HSQLDB in In_Process mode. In this mode, you can embed the HSQLDB into your web application and it runs as a part of your web application programm in the same JVM. In this mode, the database does not open any port to connect to the application on the hosing machine and you don't need to configure anything to access it. Database is not expposed to other application and can not be accessed from any dabase tools like dbVisualizer etc. In this mode ,database will be unknown from any other person except you. But in the 1.8.0 version, you can use Server intance for external as well as in process access.  To close the databse, you can issue SHUTDOWN command as an SQL query.   In the in-process mode, database starts from JDBC with the associated databse file provided through  connection URL. for example   DriverManager.getConnection("jdbc:hsqldb:mydatabase","SA","");   Here myd