Skip to content
Advertisement

Spring Boot thymeleaf bad request 400 instead of showing user error

I am trying to submit a form using post request and first validate inputs.

However when I make bad inputs (for example all empty) instead of showing error I get bad request (400).

For showing error I am using th:if and th:errors tags in HTML.

If I submit all valid inputs, there is no problem.

Controller class:

@Controller
@RequestMapping(path = "/order")
public class PurchaseController
{
    @GetMapping(path = "/new")
    public String newOrder(Model model)
    {
        model.addAttribute("Purchase", new Purchase());
        return "neworder";
    }

    @PostMapping(path = "/new")
    public String createPurchase(@Valid @ModelAttribute(name = "Purchase") Purchase purchase)
    {
        int purchaseId = 0;
        try
        {
            purchaseId = PurchaseManager.insertPurchase(purchase);
        }
        catch (SQLException e)
        {
            return "purchaseerror";
        }
        if (purchaseId == 0)
            return "purchaseerror";
        return "redirect:/order/success?id=" + purchaseId;
    }

    @GetMapping(path = "/success")
    public String successPurchase(@RequestParam(required = true, name = "id") int id, Model model)
    {
        model.addAttribute("id", id);
        return "ordersuccess";
    }
}

HTML form:

<form th:action="@{new}" th:object="${Purchase}" method="post">
                <table>
                    <tr>
                        <td>First name:</td>
                        <td><input type="text" th:field="*{firstName}" /></td>
                        <td th:if="${#fields.hasErrors('firstName')}"
                            th:errors="*{firstName}">Must be filled</td>
                        <td>Last name:</td>
                        <td><input type="text" th:field="*{lastName}" /></td>
                        <td th:if="${#fields.hasErrors('lastName')}"
                            th:errors="*{lastName}">Must be filled</td>
                    </tr>
                    <tr>
                        <td>Adresa:</td>
                        <td><input type="text" th:field="*{address}" /></td>
                        <td th:if="${#fields.hasErrors('address')}" th:errors="*{address}">Must be filled</td>
                    </tr>
                    <tr>
                        <td>ico:</td>
                        <td><input type="text" th:field="*{ico}" /></td>
                        <td th:if="${#fields.hasErrors('ico')}" th:errors="*{ico}">Must be filled</td>
                        <td>dic:</td>
                        <td><input type="text" th:field="*{dic}" /></td>
                        <td th:if="${#fields.hasErrors('dic')}" th:errors="*{dic}">Must be filled</td>
                    </tr>
                    <tr>
                        <td>Email:</td>
                        <td><input type="text" th:field="*{email}" /></td>
                        <td th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Must be filled</td>
                        <td>phone:</td>
                        <td><input type="text" th:field="*{phone}" /></td>
                        <td th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}">Must be filled</td>
                    </tr>
                </table>
                <input type="submit" value="Submit"/>
            </form>

Model class (Purchase)

public class Purchase
{
    private int id;

    @NotBlank
    @Size(max = 50)
    private String firstName;

    @NotBlank
    @Size(max = 50)
    private String lastName;

    @NotBlank
    @Size(max = 50)
    private String ico;

    @NotBlank
    @Size(max = 50)
    private String dic;

    @NotBlank
    @Size(max = 400)
    private String address;

    @NotBlank
    @Size(max = 50)
    private String email;

    @NotBlank
    @Size(max = 50)
    private String phone;

    private LocalDateTime creationDate;

    ... getters and setters, constructors

How to make showing error using thymeleaf work?

EDIT: I managed to make it work by adding BindingResult parameter to my post method in Controller class and checking if there are any errors. If yes, i return same page the form is on (/new mapping), which is “neworder”.

return “purchaseerror”; might created a bit of confusion.

@PostMapping(path = "/new")
    public String createPurchase(@Valid @ModelAttribute(name = "Purchase") Purchase purchase, BindingResult result)
    {
        if (result.hasErrors())
        {
            return "neworder";
        }
        int purchaseId = 0;
        try
        {
            purchaseId = PurchaseManager.insertPurchase(purchase);
        }
        catch (SQLException e)
        {
            return "redirect:/purchaseerror";
        }
        if (purchaseId == 0)
            return "redirect:/purchaseerror";
        return "redirect:/order/success?id=" + purchaseId;
    }

Advertisement

Answer

I think your problem could be resolved if you use Model as your 2nd parameter in the createPurchase method. Then inside your method you could do something like the following to add messages:

@PostMapping("/add")
    public String addUser(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "errors/addUser";
        }
        repository.save(user);
        model.addAttribute("users", repository.findAll()); //this is what you could do.
        return "errors/home";
    }

which would kind of result your method as following (please do amend it your discretion — I am only writing for demonstrative purposes):

@PostMapping(path = "/new")
    public String createPurchase(@Valid @ModelAttribute(name = "Purchase") Purchase purchase, Model model)
    {
        int purchaseId = 0;
        try
        {
            purchaseId = PurchaseManager.insertPurchase(purchase);
        }
        catch (SQLException e)
        {
            // todo: don't return right away. Add `model.addAttribute` first.
            return "purchaseerror";
        }
        if (purchaseId == 0) {
            // todo: don't return right away. Add `model.addAttribute` first.
            return "purchaseerror";
        }
        
        return "redirect:/order/success?id=" + purchaseId;
    }

The added values in the modelAttribute would then be picked by your Thymeleaf implementation from where you could pick the errors (as you would have populated it) and simply base logic upon that.

You can follow example from here for a better understanding. Just remember that you need to add into the Model before you can lay out logic in your thymeleaf based upon that.

I hope my answer resolves your queries. If not, apologies.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement