title: Trouble validating form on POST request (Springboot)
body: <p>I'm having troubling validating my form. It should watch out for null or empty selectedDate and description fields but I'm getting the following error instead of the validation messages I expected:</p>
  
  <pre><code>There was an unexpected error (type=Bad Request, status=400).
  Validation failed for object='requestModel'. Error count: 2
  </code></pre>
  
  <p><strong>Form:</strong></p>
  
  <pre><code>&lt;form role="form" th:action="@{/request/save}" th:object="${requestModel}" method="post"&gt;
  
      &lt;input type="checkbox" th:field="*{hasForced}" th:checked="${false}" style="display: none;"/&gt;
      &lt;p&gt;&lt;input id="description" class="descriptionField" type="text" th:field="*{description}"
                placeholder="Please provide a reason for your request"
                style="width: 500px; border-radius: 4px; padding: 11px 11px 11px 11px;"/&gt;&lt;/p&gt;
      &lt;input id="embeddedDateField" class="dateField" placeholder="YYYY-MM-DD" type="text" th:field="*{selectedDate}" readonly
             style="border-radius: 4px; background: #eefdff; text-align: center;"/&gt;&lt;br&gt;
      &lt;input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/&gt;
      &lt;div style="margin: 5px; width: 200px;"&gt;&lt;input type="submit" value="Submit Request"
                                                     style="display: block;"&gt;&lt;/div&gt;
      &lt;p th:if="${#fields.hasErrors('description')}" th:errors="*{description}"&gt;Description Error&lt;/p&gt;
      &lt;p th:if="${#fields.hasErrors('selectedDate')}" th:errors="*{selectedDate}"&gt;Date Error&lt;/p&gt;
  
  &lt;/form&gt;
  </code></pre>
  
  <p><strong>Entity:</strong></p>
  
  <pre><code>public class RequestModel {
  
      private Long requestId;
  
      @NotNull
      @Min(1)
      private String selectedDate;
  
      private RequestStatus requestStatus;
  
      @NotNull
      @Min(1)
      private String description;
  
      private Boolean hasForced;
  
      public String getSelectedDate() {
          return selectedDate;
      }
  
      public void setSelectedDate(String selectedDate) {
          this.selectedDate = selectedDate;
      }
  
      public Long getRequestId() {
          return requestId;
      }
  
      public void setRequestId(Long requestId) {
          this.requestId = requestId;
      }
  
      public RequestStatus getRequestStatus() {
          return requestStatus;
      }
  
      public void setRequestStatus(RequestStatus requestStatus) {
          this.requestStatus = requestStatus;
      }
  
      public String getDescription() {
          return description;
      }
  
      public void setDescription(String description) {
          this.description = description;
      }
  
      public Boolean getHasForced() {
          return hasForced;
      }
  
      public void setHasForced(Boolean hasForced) {
          this.hasForced = hasForced;
      }
  }
  </code></pre>
  
  <p><strong>Stack Trace:</strong></p>
  
  <pre><code>java.text.ParseException: Unparseable date: ""
      at java.text.DateFormat.parse(DateFormat.java:366)
      at com.test.controller.RequestController.saveRequest(RequestController.java:75)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:498)
      at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
      at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
      at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:116)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:826)
      at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:737)
      at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
      at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
      at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
      at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:964)
      at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:867)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
      at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:841)
      at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:230)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317)
      at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127)
      at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331)
      at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214)
      at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
      at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
      at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
      at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
      at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:192)
      at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
      at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
      at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
      at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:474)
      at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
      at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
      at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
      at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:349)
      at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:783)
      at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
      at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:798)
      at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1434)
      at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
      at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
      at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
      at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
      at java.lang.Thread.run(Thread.java:745)
  </code></pre>
  
  <p>I'm getting the error at this line <code>formattedDate = formatter.parse(requestModel.getSelectedDate());</code> in the controller below because obviously the date is null or empty when you press submit without providing any input. But isn't this where the form validation should kick in? Instead of redirecting to the error page, should it not immediately refresh the current page and show the warnings I provided, i.e. <code>&lt;p th:if="${#fields.hasErrors('selectedDate')}" th:errors="*{selectedDate}"&gt;Date Error&lt;/p&gt;</code>?</p>
  
  <p>Here is the controller:</p>
  
  <pre><code>@Controller
  @RequestMapping("/request")
  public class RequestController {
  
          @RequestMapping(value = "/save", method = RequestMethod.POST)
              String saveRequest(Principal principal, @ModelAttribute(value = "requestModel") @Valid RequestModel requestModel, RedirectAttributes redirectAttributes, BindingResult bindingResult) {
  
                  // Refresh the page if there are errors
                  if (bindingResult.hasErrors()) {
                      return "request";
                  }
  
                  // Create New RequestDO Object
                  Users user = usersRepository.findOneByInitialName(principal.getName());
  
                  // Format given date
                  Date formattedDate = null;
                  try {
                      SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
                      formattedDate = formatter.parse(requestModel.getSelectedDate());
  
                  } catch (ParseException e) {
                      e.printStackTrace();
                      return "error";
                  }
  
                  if(!requestModel.getHasForced() &amp;&amp; !isFirstHO(user, formattedDate)){
                      redirectAttributes.addFlashAttribute("requestModel", requestModel);
                      return "redirect:/forceRequest";
                  }
  
                  RequestDO requestDOOb = new RequestDO();
  
                  // Set UserId to RequestDO Field USER_ID
                  requestDOOb.setUsers(user);
  
                  // Set Additional RequestDO Fields
      requestDOOb.setDescription(!StringUtils.isEmpty(requestModel.getDescription()) ? requestModel.getDescription() : "I would like to make a request.");
                  requestDOOb.setStatus(RequestStatus.PENDING);
                  requestDOOb.setRequestDate(formattedDate);
  
                  // Save RequestDO Object
                  RequestDO sentRequest = requestRepository.save(requestDOOb);
  
                  return "redirect:/requests";
              }
  }
  </code></pre>
  
  <p><strong>Important. Final Note:</strong> Both of the answers below constitute a combined answer to my problem. There was an exception initially which prevented access to the bindingResult block. Second, the order of the parameters in the POST request is very important. Please see both below.</p>