Solution 1 :

To pass the custom object to controller using form we should make a converter.
If your object has an id, then you can make it like in this topic(https://stackoverflow.com/a/60066445/14308420).
But if you have object that doesn’t exist at your DB, then you can follow this guide(https://www.baeldung.com/spring-type-conversions).
An example is in the code below.
In my case, I solved this problem without passing list of ordered products as a field in a form by autowiring ShoppingCart in OrderController.

Example:

public class Employee {

    private long id;
    private double salary;

    // standard constructors, getters, setters
}


public class StringToEmployeeConverter
  implements Converter<String, Employee> {

    @Override
    public Employee convert(String from) {
        String[] data = from.split(",");
        return new Employee(
          Long.parseLong(data[0]), 
          Double.parseDouble(data[1]));
    }
}


@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEmployeeConverter());
    }
}

Problem :

I have a problem with thymeleaf form post. It occurs when I’m trying to send my ShoppingCart object to controller.
Project details:
There is /products endpoint, where user can add desired products to his ShoppingCart(session).
In cart the added products will be transformed to new entity OrderedProduct, which has field product and quantity.
When user is ready, he goes to /cart endpoint to confirm deal and send order.

So, then I send post request to /orders endpoint. But in controller I get an empty cart with null fields(orderedProducts, total).
As I understand that is expected behaviour of thymeleaf. And in order to pass the params from my cart I should explicitly define them as hidden fields at form.
So I added this:

<input type="hidden" name="total" th_field="*{total}"/>
<input type="hidden" name="orderedProducts" th_field="*{orderedProducts}"/>

But then I get such exception:

Field error in object 'shoppingCart' on field 'orderedProducts': rejected value [null,null];
codes [typeMismatch.shoppingCart.orderedProducts,typeMismatch.orderedProducts,typeMismatch.java.util.List,typeMismatch];
arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [shoppingCart.orderedProducts,orderedProducts];arguments [];
default message [orderedProducts]];default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.List' for property 'orderedProducts';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Long] for value 'null,null';
nested exception is java.lang.NumberFormatException: For input string: "null,null"]]

As I understand it’s conversion problem and we should define special converter for OrderedProduct(as in this topic: https://stackoverflow.com/a/60066445/14308420).
Considering that OrderedProduct is stored in session and yet doesn’t have an id, how to make converter?

checkout.html

<form th_action="@{/orders}" th_object="${cart}" method="post">
  <input type="hidden" name="total" th_field="*{total}"/>
  <input type="hidden" name="orderedProducts" th_field="*{orderedProducts}"/>
  <label for="phoneNumber">Phone number: </label>
  <input type="number" required="required" name="phoneNumber"
                   class="form-control" id="phoneNumber">
  <button type="submit" class="btn-lg btn-primary">Order</button>
</form>

OrderUIController

@PostMapping
  public String create(@ModelAttribute ShoppingCart cart,
      @RequestParam("phoneNumber") Long phoneNumber) {
    Order order = new Order();
    order.setOrderedProducts(cart.getOrderedProducts());
    order.setPhoneNumber(phoneNumber);
    order.setTotal(cart.getTotal());
    service.create(order);
    return "redirect:/profile";
  }   

ShoppingCart

@Component
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ShoppingCart implements Serializable {
    private List<OrderedProduct> orderedProducts = new ArrayList<>();

    private Long total;
}

OrderedProduct

public class OrderedProduct extends AbstractBaseEntity {
    private Integer quantity;

    private Product product;
}

Comments

Comment posted by Wim Deblauwe

Not directly related to your question, but you should not take the

Comment posted by leonaugust

@WimDeblauwe Thanks for that important remark. How we can secure field total? Considering that user can change the quantity of the products in cart at checkout.html and field total will be changed accordingly.

Comment posted by Wim Deblauwe

Thanks for the tip about my profile, fixed now. About the total: You should have the server calculate the total again from the products in the cart. You could remove the

By