Search code examples
javaspringspring-bootthymeleafspring-thymeleaf

springBoot + Thymeleaf: validate Email


I have this object:

@Builder
@Data
@AllArgsConstructor
@NoArgsConstructor
public class NatalChartDataPayload {
    String langCode;

    @NotEmpty(message = "Email cannot be empty")
    @Email(message = "Invalid email format")
    String email;

    String city;
    String place;
}

Controller look like:

@GetMapping({"/data"})
public String data (Model model) {
    model.addAttribute("months", Month.values());
    model.addAttribute("data",   NatalChartDataPayload.builder().build());
    return "natalChartData";
}

@PostMapping("/create")
public String createNatalChart ( 
    @Valid @ModelAttribute NatalChartDataPayload data,
    BindingResult result, 
    Model model
) {        
    if (result.hasErrors()) {
        return "natalChartData";
    }
        
    User user = new User();    
    user.setEmail(data.getEmail());
               
    if (data.getPlace() == null ||
          data.getPlace().isEmpty() ||
          data.getEmail() == null ||
          data.getEmail().isEmpty()) {
    return "redirect:/natalchart/data";
}

However, when the email is not correct I have this error:

aused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "#fields.hasErrors('email')" (template: "natalChartData" - line 142, col 20)
    at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
    at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
    at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
    ... 101 more
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "#fields.hasErrors('email')" (template: "natalChartData" - line 142, col 20)
    at org.thymeleaf.spring6.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292)
    at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:125)
    at org.thymeleaf.standard.processor.StandardIfTagProcessor.isVisible(StandardIfTagProcessor.java:59)
    at org.thymeleaf.standard.processor.AbstractStandardConditionalVisibilityTagProcessor.doProcess(AbstractStandardConditionalVisibilityTagProcessor.java:61)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
    at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
    at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
    at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304)
    at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278)
    at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleOpenElementEnd(OutputExpressionInlinePreProcessorHandler.java:186)
    at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:124)
    at org.attoparser.HtmlElement.handleOpenElementEnd(HtmlElement.java:109)
    at org.attoparser.HtmlMarkupHandler.handleOpenElementEnd(HtmlMarkupHandler.java:297)
    at org.attoparser.MarkupEventProcessorHandler.handleOpenElementEnd(MarkupEventProcessorHandler.java:402)
    at org.attoparser.ParsingElementMarkupUtil.parseOpenElement(ParsingElementMarkupUtil.java:159)
    at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:710)
    at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301)
    ... 103 more
Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'data' available as request attribute
    at org.springframework.web.servlet.support.BindStatus.<init>(BindStatus.java:153)
    at org.springframework.web.servlet.support.RequestContext.getBindStatus(RequestContext.java:926)
    at org.thymeleaf.spring6.context.webmvc.SpringWebMvcThymeleafRequestContext.getBindStatus(SpringWebMvcThymeleafRequestContext.java:232)
    at org.thymeleaf.spring6.util.FieldUtils.getBindStatusFromParsedExpression(FieldUtils.java:306)
    at org.thymeleaf.spring6.util.FieldUtils.getBindStatus(FieldUtils.java:253)
    at org.thymeleaf.spring6.util.FieldUtils.getBindStatus(FieldUtils.java:227)
    at org.thymeleaf.spring6.util.FieldUtils.checkErrors(FieldUtils.java:212)

here is the template:

 <form method="post" th:action="@{/natalchart/create}" th:object="${data}">
          <div class="row">
            <div class="col-lg-8">
                <h4>Enter your birth details to create your natal chart</h4>

              <p>&nbsp;</p>
              <p>&nbsp;</p>

              <div th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</div>

                <div>
                  <label for="language">PDF Report Language</label>
                  <select th:field="*{langCode}">
                    <option value="en">English</option>
                    <option value="es">Spanish</option>
                    <option value="fr">French</option>
                    <option value="pt">Portuguese</option>
                  </select><br><br>

                  <label for="email">Email</label>
                  <input id="email" name="email" placeholder="email" th:field="*{email}"  /><br><br>

                  <!-- Label and select fields for day, month, and year of birth -->
                  <label for="dayOfBirth">Date of Birth:</label>
                  <select id="dayOfBirth" th:field="*{dayOfBirth}">
                    <option th:each="day : ${#numbers.sequence(1, 31)}" th:text="${day}" th:value="${day}"></option>
                  </select>
                  <select id="monthOfBirth" th:field="*{monthOfBirth}">
                    <option th:each="month : ${months}" th:text="${month}" th:value="${month.getValue()}"></option>
                  </select>
                  <select id="yearOfBirth" th:field="*{yearOfBirth}">
                    <option th:each="year : ${#numbers.sequence(1900, 2025)}" th:text="${year}" th:value="${year}"></option>
                  </select>
                  <br><br>
                  <!-- Label and select fields for hour and minute of birth -->
                  <label for="hourOfBirth">Time of Birth:</label>
                  <select id="hourOfBirth" th:field="*{hourOfBirth}">
                    <option th:each="hour : ${#numbers.sequence(0, 23)}" th:text="${hour}" th:value="${hour}"></option>
                  </select>
                  <select id="minuteOfBirth" th:field="*{minuteOfBirth}">
                    <option th:each="minute : ${#numbers.sequence(0, 59)}" th:text="${minute}" th:value="${minute}"></option>
                  </select>
                  <br><br>
                  <label for="hint">Place of birth (Select one of the list)</label>
                  <input id="hint"  name="hint"   th:field="*{city}" /><br><br>
                  <input id="place" name="place" th:field="*{place}" type="hidden"/>
                </div>
          </div>
            <div class="col-lg-4">
              <div class="blog-sidbar">
                    <div class="d-flex justify-content-between mt-20">
                      <button class="fill-btn" data-text="Send Message" type="submit">create PDF Natal Chart Report</button>
                    </div>
              </div>
            </div>
        </div>
</form>

Solution

  • According to the last cause in your error trace:

    Caused by: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'data' available as request attribute
    

    the problem you are facing could be caused by the absence of the data attribute in the request.

    According to your controller, in case of validation errors, you instruct Spring to redirect the request to your HTML template page again, but you don't provide the required data attribute.

    Probably the problem could be solved in different ways but please, try the following, I think it should work properly:

    @PostMapping("/create")
    public String createNatalChart ( 
        @Valid @ModelAttribute NatalChartDataPayload data,
        BindingResult result, 
        Model model
    ) {        
        if (result.hasErrors()) {
            // Add data to the returned model. Probably you will need
            // to add the attribute months as well
            model.addAttribute("data", data);
            return "natalChartData";
        }
            
        User user = new User();    
        user.setEmail(data.getEmail());
                   
        if (data.getPlace() == null ||
              data.getPlace().isEmpty() ||
              data.getEmail() == null ||
              data.getEmail().isEmpty()) {
        return "redirect:/natalchart/data";
    }