0

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:

There was an unexpected error (type=Bad Request, status=400).
Validation failed for object='requestModel'. Error count: 2

Form:

<form role="form" th:action="@{/request/save}" th:object="${requestModel}" method="post">

    <input type="checkbox" th:field="*{hasForced}" th:checked="${false}" style="display: none;"/>
    <p><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;"/></p>
    <input id="embeddedDateField" class="dateField" placeholder="YYYY-MM-DD" type="text" th:field="*{selectedDate}" readonly
           style="border-radius: 4px; background: #eefdff; text-align: center;"/><br>
    <input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}"/>
    <div style="margin: 5px; width: 200px;"><input type="submit" value="Submit Request"
                                                   style="display: block;"></div>
    <p th:if="${#fields.hasErrors('description')}" th:errors="*{description}">Description Error</p>
    <p th:if="${#fields.hasErrors('selectedDate')}" th:errors="*{selectedDate}">Date Error</p>

</form>

Entity:

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;
    }
}

Stack Trace:

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)

I'm getting the error at this line formattedDate = formatter.parse(requestModel.getSelectedDate()); 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. <p th:if="${#fields.hasErrors('selectedDate')}" th:errors="*{selectedDate}">Date Error</p>?

Here is the controller:

@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() && !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";
            }
}

Important. Final Note: 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.

2 Answers 2

1

Inside your try catch block you need to do following:

        try {
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
            formattedDate = formatter.parse(requestModel.getSelectedDate());
        } catch (ParseException e) {
            e.printStackTrace();
            bindingResult.rejectValue("selectedDate", "Invalid Date");
            return "request";
        }
Sign up to request clarification or add additional context in comments.

2 Comments

Well that certainly fixes the exception! But unfortunately, not my problem. Now I get absolutely no error or stack trace but I'm still redirecting to an error page with There was an unexpected error (type=Bad Request, status=400). Validation failed for object='requestModel'. Error count: 2. I should mention I'm getting this even if these fields are not empty. When I remove all the validation annotations the form is submitted successfully. I followed this guide: spring.io/guides/gs/validating-form-input/#initial
Also with debugging I can see that I never get inside if (bindingResult.hasErrors()) { log.info("There are binding errors."); return "request"; }
0

I changed the message signature of my POST request. Apparently the order of @Valid, @ModelAttribute and BindingResult are very sensitive:

@RequestMapping(value = "/save", method = RequestMethod.POST)
    String saveRequest(Principal principal, @Valid @ModelAttribute(value = "requestModel") RequestModel requestModel, BindingResult bindingResult, RedirectAttributes redirectAttributes) {

        if (bindingResult.hasErrors()) {
            log.info("There are binding errors.");
            return "send";
        }
 ...
 }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.