javaspring-bootthymeleaf

How to display a Java variable onto ThymeLeaf HTML using the span tag?


I'm currently writing a project that incorporates the use of ThymeLeaf HTML for the frontend and Java for the backend. I'm trying to display a variable from one of my Java controller classes onto my HTML page using ThymeLeaf. The variable is essentially the username and it's only to be displayed once the user logs in and is redirected back to the main page. The error that I'm getting here is that it can't resolve username. So, the variable is clearly not being read in my ThymeLeaf HTML file for some reason. The syntax seems to be right based on what I've read from the ThymeLeaf documentation. And I've been told that it should be working, but it's not working for whatever reason. Anyways, I'm wondering what is causing this issue to occur because I'm not sure why it's not being read in the first place? It keeps telling me that it can't resolve username despite the fact that I have the variable in my controller class. The code doesn't have any errors as far as my controller class goes. The code is posted down below which includes the ThymeLeaf HTML page where I'm trying to display the username if the user logs in. And the second part is the controller class for the user login.

  <div class="d-flex justify-content-between align-items-center mb-3">
    <h1>Auto Parts</h1>
        <a th:href="@{/login}" class="btn btn-primary btn-sm mb-3">Login</a>
    <a th:href="@{/about}" class="btn btn-primary btn-sm mb-3" >About Us</a>
        <span th:if="${username}" class="ms-3">Welcome, <span th:text="${username}">User</span>!</span>

    </div>
@Controller
public class LoginController {

    private static final String HARDCODED_USERNAME = "Carter";
    private static final String HARDCODED_PASSWORD = "Password907";

    @GetMapping("/login")
    public String login() {
        return "login";
    }

    @PostMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password, Model model) {


        if (username.equals(HARDCODED_USERNAME) && password.equals(HARDCODED_PASSWORD)) {
            model.addAttribute("username", username);
            return "redirect:/mainscreen";
        } else  {
          model.addAttribute("loginError", true);
            return "login";
        }
    }
    // Mapping for mainscreen page
    @RequestMapping("/mainscreen")
    public String mainscreen(Model model) {
        return "mainscreen"; // returns mainscreen.html
    }

}

I've tried to just display the variable without using the if statement. And this didn't work either because it somehow won't read the variable. I've tried changing my model attribute name and it didn't work either. Nothing I did worked and I can't seem to figure out why this is an issue for me because it should be working without any issues.


Solution

  • I want to start by saying that the solution I am about to provide you with is absolutely NOT the way you should be doing it (cause it is not secure and in general not a good idea to implement your own security). What you should be doing is add spring-security and thymeleaf-extras-springsecurity6 dependencies and then use the provided Authentication object in your template like shown here

    But since you are just learning and adding spring security as a beginner will just result in nothing working anymore for you (not true, but it will seem that way:), here is how you could implement it for STUDY PURPOSES ONLY. You absolutely should change your solution to use spring security later.

    Basically you want to be able to remember the User based on his session (in case he logged in or logged out). Start by implementing a session scoped bean (will only live for a session, and each session will get its own)

    @SessionScope
    @Component
    public class MyPrincipal {
        private String username;
        // getter, setter
    }
    

    then autowire it into your controller and add it to the model:

    @Controller
    public class LoginController {
        @Autowired
        private MyPrincipal myPrincipal;
    
        // ...
    
        @PostMapping("/login")
        public String login(@RequestParam String username, @RequestParam String password, Model model) {
    
            if (username.equals(HARDCODED_USERNAME) && password.equals(HARDCODED_PASSWORD)) {
                myPrincipal.setUsername(username); // you should also set it to null at logout
                return "redirect:/mainscreen";
            } else  {
              model.addAttribute("loginError", true);
                return "login";
            }
        }
        
        @ModelAttribute("username")
        public String username() {
            return myPrincipal.getUsername();
        } 
    }
    

    @ModelAttribute adds the specified attribute for each request that lands in this controller so it would make sense to only keep controller methods that you want this username to be added and move the other ones to some other controller - but that is a design decision.


    Like I mentioned before this is kind of reinventing the wheel in a not secure way but I remember how I was starting out and I also started by learning with thymeleaf and then upgraded my solution to use spring security later, so hopefully this provides educational value