javahtmlspringthymeleafinternal-server-error

spring boot An error happened during template parsing


@Controller
@RequestMapping("/products")
public class ProductsController {
    @Autowired
    private ProductsRepository repo;

    @GetMapping({"", "/"})
    public String showProductList(Model model) {
        List<Product> products = repo.findAll(Sort.by(Sort.Direction.DESC, "id"));
        model.addAttribute("products", products);
        return "products/index";
    }
}
//properties
spring.application.name=beststore
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/beststore
spring.datasource.username=root
spring.datasource.password=9874156R
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
resources/static/index.html

<body>
<div class="container">
  <h1 class="text-center my-4">Welcome to our website</h1>

  <a class="btn btn-primary" href="/products">Products</a>
</div>

resources/templates/products/index.html

<body>
<div class="container">
    <h1 class="text-center my-4">Products</h1>

    <a class="btn btn-primary" href="/products/create">Create Product</a>

    <table class="table">
        <thead>
        <tr>
            <th>ID</th>
            <th>Name</th>
            <th>Brand</th>
            <th>Category</th>
            <th>Price</th>
            <th>Image</th>
            <th>Create At</th>
            <th>Action</th>
        </tr>
        </thead>
        <tbody>
        <tr th:each="product : ${products}">
            <td th:text="${product.id}"></td>
            <td th:text="${product.name}"></td>
            <td th:text="${product.brand}"></td>
            <td th:text="${product.category}"></td>
            <td th:text="${product.price} + '$'"></td>
            <td>
                <img th:src="@{'/images/' + ${product.imageFilename}}" alt="..." width="100">
            </td>
            <td th:text="${#dates.format(product.createdAt, 'yyyy-MM-dd')}"></td>
            <td style="white-space: nowrap">
                <a class="btn btn-primary btn-sm" th:href="@{/products/edit(id=${product.id})}">Edit</a>
                <a class="btn btn-danger btn-sm" th:href="@{/products/delete(id=${product.id})}"
                   onclick="return confirm('Are you sure?')">Delete</a>
            </td>
        </tr>
        </tbody>
    </table>
</div>

there is no problem when i open http://localhost:8080 i can get index page but when i click products button i get redirected to http://localhost:8080/products and there is this error

Whitelabel Error Page

This application has no explicit mapping for /error, so you are seeing this as a fallback.

Wed Jan 15 13:53:26 AZT 2025

There was an unexpected error (type=Internal Server Error, status=500).

An error happened during template parsing (template: "class path resource [templates/products/index.html]")

org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/products/index.html]")

Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "product.id" (template: "products/index" - line 31, col 17) 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) ... 48 more

Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "product.id" (template: "products/index" - line 31, col 17)
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'id' cannot be found on object of type 'com.example.beststore.Model.Product' - maybe not public or not valid?


Solution

  • You have it written in stacktrace --

    Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "product.id"
    

    On your object product is probably id not accessible as property, and that is correct, correct way is to use getter/setters. So replace all accessors to properties with getters and it should be ok.