javaspringvalidationmodel-view-controllerfreemarker

Freemarker.core.InvalidReferenceException error when trying to validate user input Apache FreeMarker


There is such ftlh file with registration form:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Rental Property</title>
</head>
<body>
<h2>User Registration</h2>
<form action="/registration" method="POST">

    <label for="name">Name:</label>
    <input type="text" id="name" name="name" value="${userForm.name!}" required>
    <#if errors?? && errors?has_content && errors.hasFieldErrors("name")>
        <div style="color:red;">${errors.getFieldError('name').defaultMessage}</div>
    </#if>

    <label for="lastName">Last Name:</label>
    <input type="text" id="lastName" name="lastName" value="${userForm.lastName!}" required>
    <#if errors?? && errors?has_content && errors.hasFieldErrors("lastName")>
        <div style="color:red;">${errors.getFieldError("lastName").defaultMessage}</div>
    </#if>

    <label for="email">Email Address:</label>
    <input type="email" id="email" name="email" value="${userForm.email!}" required>
    <#if errors?? && errors?has_content && errors.hasFieldErrors("email")>
        <div style="color:red;">${errors.getFieldError("email").defaultMessage}</div>
    </#if>

    <label for="phone">Phone:</label>
    <input type="tel" id="phone" name="phone" value="${userForm.phone!}">
    <#if errors?? && errors?has_content && errors.hasFieldErrors("phone")>
        <div style="color:red;">${errors.getFieldError("phone").defaultMessage}</div>
    </#if>

    <label for="password">Password:</label>
    <input type="password" id="password" name="password" required>
    <#if errors?? && errors?has_content && errors.hasFieldErrors("password")>
        <div style="color:red;">${errors.getFieldError("password").defaultMessage}</div>
    </#if>

    <label for="replayPassword">Repeat Password:</label>
    <input type="password" id="replayPassword" name="replayPassword" required>
    <#if errors?? && errors?has_content && errors.hasFieldErrors("replayPassword")>
        <div style="color:red;">${errors.getFieldError("replayPassword").defaultMessage}</div>
    </#if>

    <label for="birthday">Date of Birth:</label>
    <input type="date" id="birthday" name="birthday">
    <#--    <#if errors?has_content && errors.hasFieldErrors("birthday")>-->
    <#--        <div style="color:red;">${errors.getFieldError("birthday").defaultMessage}</div>-->
    <#--    </#if>-->

    <button type="submit">Register</button>
</form>
</body>
</html>

There is such a method of post request processing:

@PostMapping("/registration")
    public String registration(@ModelAttribute @Valid UserForm userForm,
                               BindingResult result, Model model)
    {
        userValidator.validate(userForm, result);
        model.addAttribute("errors", result);
        if (result.hasErrors())
            return "registration";
        return "redirect:/";
    }

Above each field of the UserForm.java class are validation annotations from jakarta.validation.constraints and also a custom validator class. When trying to send data with errors inside this controller method the validation works as it should, all errors are looked for and an error occurs when trying to display incorrect data to the user:

freemarker.core.InvalidReferenceException: The following has evaluated to null or missing:
==> errors.getFieldError('name')  [in template "registration.ftlh" at line 14, column 35]

----
Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)??
----

----
FTL stack trace ("~" means nesting-related):
    - Failed at: ${errors.getFieldError("name").defaul...  [in template "registration.ftlh" at line 14, column 33]
----
    at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134) ~[freemarker-2.3.33.jar:2.3.33]
    at freemarker.core.UnexpectedTypeException.newDescriptionBuilder(UnexpectedTypeException.java:85) ~[freemarker-2.3.33.jar:2.3.33]...

I tried changing the error key in the ftlh file to single quotes, changing the validation, sending the entire assembled UserForm object.


Solution

  • Use something other than FreeMarker