How to validate date format in a Spring form

22,869

I don't know if there is a direct way to do it in spring, but the way I have done it is a combination of jQuery's DatePicker and InitBinder.

In the JS side, you create a:

<form:input cssClass="datepicker" path="someProperty" readonly="true" />

Then in the JS:

$(document).ready(function() {
    $('.datepicker').datepicker();
});

On the Controller side, create a method like thus:

@InitBinder
public void initBinder(WebDataBinder binder) {
    SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
    sdf.setLenient(true);
    binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
}

From here you can create typeMismatch messages in your resource bundle to create a nice, pretty message. The user will not be able to manually type into the field, but instead will only be able to use the jQuery DatePicker, which will format the date as needed (I think the default is MM/dd/yyyy). In the case where they DO manage to enter a date, Spring will use the configured CustomEditor to convert the String from the view to the Date expected. If it fails, you get a error in the BindingResults (if you include it in your method signature). You can customize this method, as I stated before, but setting up a custom typeMismatch message in your resource bundle.

Edit: Adding additional details as my explaination above was obviously not clear enough...

First, create a Bean or something to act as your Model Attribute (what you send back and forth from View to Controller). Ensure it has at least one date in it.

public class SomeBean {
    private Date someDate;
    // ...additional properties, getters, setters...
    public Date getSomeDate() { return someDate; }
    public void setSomeDate(Date date) { somedate = date; }
}

Now you need a controller. I like to make my Model Attributes session attributes via the

@SessionAttribute.
@Controller
@RequestMapping("/somePath")
@SessionAttributes({"someFormBean"})
public class SomeController {
    /**
     * Handler method
     */
    @RequestMapping()
    public String defaultView(@ModelAttribute("someFormBean") SomeBean someFormBean, Model uiModel) {
        // Do something in your default view
        return "someDefaultView";   // Assuming you have a ViewResolver, like JspViewResolver or Tiles
    }

    /**
     * Submission Handler method
     */
    @RequestMapping(method = RequestMethod.POST
    public String submit(
        @ModelAttribute("someFormBean") SomeBean someFormBean, 
        BindingResult bindingResults,
        Model uiModel) {
        // bindingResults will have your errors from binding
        if(bindingResults.hasErrors()) {
            return "errorView";
        } else {
            return "successView";
        }
    }

    /**
     * Will be called to create your Model Attribute when its missing
     */
    @ModelAttribute("someFormBean")
    public SomeBean createSomeBean() {
        return new SomeBean();
    }

    /**
     * register property editors
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {
        SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
        sdf.setLenient(true);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(sdf, true));
        // You can register other Custom Editors with the WebDataBinder, like CustomNumberEditor for Integers and Longs, or StringTrimmerEditor for Strings
    }   
}

Then you need some view ("someDefaultView" above in the controller, my code is JSP in this example, using the Spring JSTL tag library)

<%@ taglib prefix="c"       uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt"     uri="http://java.sun.com/jsp/jstl/fmt" %>
<%@ taglib prefix="fn"      uri="http://java.sun.com/jsp/jstl/functions" %>
<%@ taglib prefix="form"    uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="spring"  uri="http://www.springframework.org/tags" %>
<html>
    <head>
        <script type="text/javascript" src="/resources/jquery/1.7.1/jquery-1.7.1.min.js"></script>
        <script type="text/javascript" src="resources/jquery.ui/1.8.13/jquery.ui.min.js"></script>

        <script type="text/javascript">
            $(document).ready(function() {
                $('.datepicker').datepicker();
            };
        </script>
    </head>
    <body>
        <form:form modelAttribute="someFormBean">
            <form:input cssClass="datepicker" path="someDate" readonly="true" /><br />
            <form:errors path="a"/><br /><br />
            <input type="submit" value="Submit" />
        </form:form>
    </body>
</html>

Again, I would suggest Google'ing Spring Init Binders, cusomizing binding errors (typeMismatch), and JSR 303 for additional options for validation, most of when is well documentet here. Also, if you don't want the error below to the field, as I have done here there are ways to iterate all the errors in one spot, like putting all the errors at the top of the page. Its very configurable, and I could type probably another 20 pages worth going over all of it in depth. This should be plenty to get you started in finding good examples and documentation.

Share:
22,869
Christian Vielma
Author by

Christian Vielma

I'm a person who like to do the right thing every day. Aristotle one day said "We are what we repeatedly do.Excellence, then, is not an act, but a habit.". I truly believe in this. I'm mainly what's considered a back-end developer. I've worked on some projects, with more emphasis lately on distributed applications and enterprise systems. Lately I've been working also on systems orchestration, streaming and microservices development. I also like to talk about computer science topics on my Youtube channel "A Dev's Story"

Updated on July 09, 2022

Comments

  • Christian Vielma
    Christian Vielma almost 2 years

    I'm new to Spring, and I'm trying to create a form that validates a date format (i.e.: it accepts only dates with the format "MM/dd/yyyy" if the user puts "mm-dd-yyyy" it should show an error message).

    How can I achieve this with Spring?

    I have read a lot of posts and answers like this and this, that recommend using the @InitBinder in the controller (I tried but couldn't make it work btw). But what if I have a form with different dates? or if my controller manages multiple post request from different forms, and each one requires different dates validations?

    Currently I have this form:

    <form:form action="getReportFile.html" commandName="staticReportForm">
                <table>
                    <tr>
                        <td>Reports:</td>
                    </tr>
                    <tr>
                        <td><form:select path="report" items="${staticReports}"/>                        
                        </td>
                    </tr>
                   <tr>
                       <td>Date (MM/DD/YYYY) (empty for most recent possible):<FONT color="red"><form:errors
                                    path="date" /></FONT></td>
                   </tr>
                   <tr>
                       <td><form:input path="date" /></td>
                   </tr>
                   <tr>
                       <td><input type="submit" value="Submit" /></td>
                   </tr>
               </table>            
           </form:form>
    

    And this would be the bean backing the form (the @DateTimeFormat annotation only make it work if you put the correct format):

    public class StaticReportForm {
            @NotEmpty        
            private String report;    
            @DateTimeFormat(pattern="MM/dd/yyyy")
            private Date date;
    
        public String getReport() {
            return report;
        }
    
        public void setReport(String report) {
            this.report = report;
        }
    
        public Date getDate() {
            return date;
        }
    
        public void setDate(Date date) {
            this.date = date;
        }
    
    
    }
    
  • Christian Vielma
    Christian Vielma over 11 years
    Thanks for your time! and yes, but it could fail if the browser has js disabled, am I wrong? the only way I can see I can do it is with the "old school" getting the parameter in the controller and validating it there. In my ignorance I can't find a useful way to do it using a backing bean or validator. So if you have a large form with multiple dates and want to use a bean to back and validate it you would always have to validate the date format in the controller? (I hope I'm wrong)
  • CodeChimp
    CodeChimp over 11 years
    Thats what the InitBinder does. Spring will attempt to convert the String to the Date using the registered editor. If it fails, it fills in the BindingResults with an error message. The default one is pretty nasty, but you can override it pretty easily, as mentioned. In your handler method, you simply look to see if you have errors in the BindingResults, and if you do forward them back to the form page. Spring has was of handling BindingResult errors right on your form, and its pretty well documented. Even sets up CSS to flag the fields red and pring the message.
  • Christian Vielma
    Christian Vielma over 11 years
    How do I do that? can you post that answer for my given question?
  • CodeChimp
    CodeChimp over 11 years
    How do you do which part? If you are referring to how to handle the error messages, this is VERY well documented at static.springsource.org/spring/docs/2.5.4/reference/…
  • Christian Vielma
    Christian Vielma over 11 years
    All the way, I want to send a form with a date in a strict format, and when it is received by the controller I can be sure that it is in the correct format or otherwise an error is shown to the user.
  • CodeChimp
    CodeChimp over 11 years
    I have provided that above. The view only allows the user to pick a date, not input one. And on the slim chance they circumvent the view component, Spring will handle validating the date when it converts from String to Date. You could even through JSR303 on top to enforce additional validation. What piece of the validation is the above solution missing?