Trynkiewicz Mariusz
Trynkiewicz Mariusz

Reputation: 2772

Modify and return ModelAttribute object if validation fails

I have simple entity ...

@Entity
public class WatchedDirectory {
    @Column(nullable=false)
    @NotBlank(message="Filesystem path CANNOT be empty")
    String filesystemPath;
}

... and GET endpoint for creating one ...

    @GetMapping("/add")
    public String add(@ModelAttribute WatchedDirectory watchedDirectory) {
        return "mng-dir-add";
    }

... that shows form made in Thymeleaf, with error validation and all. Once you hit sumbmit button data goes into POST endpoint ...

    @PostMapping("/add")
    public String addExecute(@Valid @ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model) {
        if(result.hasErrors()) {
            // here I want to iterate through
            // errors and clean erroneous fields
            return "mng-dir-add";
        }
        watchedDirectory = fs.persistDirectory(watchedDirectory);
        redirect.addFlashAttribute("added", watchedDirectory);
        return "redirect:/list";
    }

... and everything is nice and dandy. When data is valid it get persisted and redirect to list is issued (POST/Redirect/GET). When data is invalid thymeleaf's error fields are populated and I list error messages below appropriate fields.

The only thing I want to change, but I can't figure out how to, is to clear some data from model.

Things I tried so far: modifying @ModelAttribute parameter, setting attributes in Model, setting attributes in RedirectAttributes. Every time I get the very same data user provided without any changes in output form, for some reason I can't change a thing. I tried also redirecting to GET method but it seems it clears slate clean, which I don't want.

If someone is interested this is how form in thymeleaf looks:

        <form id="content" action="#" th:action="@{/add}" th:object="${watchedDirectory}" method="post" class="was-validated">
            <div class="form-group has-feedback has-error">
                <label for="filesystemPath">Filesystem path:</label>
                <input th:field="*{filesystemPath}" type="text" id="filesystemPath" name="filesystemPath" class="form-control" placeholder="~/" required />
                <label th:if="${#fields.hasErrors('filesystemPath')}" th:errors="*{filesystemPath}"></label>
            </div>          
            <button type="submit" class="btn btn-outline-success">Save</button>
        </form>

required attribute on input field will shut up when provided with whitespace but there will be error message from Spring's validation. Clearing this field and returning it to user will make things more consistent than showing mixed signals such as: html5 and spring showing their errors

Any help would be really appreciated.

Upvotes: 1

Views: 454

Answers (2)

Klaus
Klaus

Reputation: 1731

You need to define a BeanPropertyBindingResult object that provides the fields having the errors. Then make a model with this results,

@PostMapping( "/add" )
public String addExecute( @Valid @ModelAttribute WatchedDirectory watchedDirectory, BindingResult result, RedirectAttributes redirect, Model model )
{
    if( result.hasErrors() )
    {
        BeanPropertyBindingResult result2 = new BeanPropertyBindingResult( watchedDirectory, theBindingResult.getObjectName() );
        for( ObjectError error : theBindingResult.getGlobalErrors() )
        {
            result2.addError( error );
        }
        for( FieldError error : theBindingResult.getFieldErrors() )
        {
            result2.addError( new FieldError( error.getObjectName(), error.getField(), null, error.isBindingFailure(), error.getCodes(), error.getArguments(), error.getDefaultMessage() ) );
        }

        model.addAllAttributes( result2.getModel() );
        return "mng-dir-add";
    }
    watchedDirectory = fs.persistDirectory( watchedDirectory );
    redirect.addFlashAttribute( "added", watchedDirectory );
    return "redirect:/list";
}

Upvotes: 1

Minar Mahmud
Minar Mahmud

Reputation: 2665

According to Validation by Using Spring’s Validator Interface from the Spring Framework Documentation:

The Validator interface works by using an Errors object so that, while validating, validators can report validation failures to the Errors object.

validate(Object, org.springframework.validation.Errors): Validates the given object and, in case of validation errors, registers those with the given Errors object.

And the Errors interface doesn't provide any API for deregistering binding errors.

So, it seems there is no Spring provided way to achieve what you want.

Upvotes: 0

Related Questions