Error resolving template [create], template might not exist or might not be accessible by any of the configured Template Resolvers - spring-boot

I can't figure out why I keep getting this status = 500 template error.
''There was an unexpected error (type=Internal Server Error, status=500).
Error resolving template [create], template might not exist or might not be accessible by any of the configured Template Resolvers
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [create], template might not exist or might not be accessible by any of the configured Template Resolvers''
I tried looking up resources and I coundn't resolve it.
This is what IntelliJ is saying:
This is my create.html template:
<html lang="en" xmlns:th="https://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<head>
<link th:href="#{/css/meetups.css}" rel="stylesheet" />
</head>
<body class="create-meetup-body">
<header th:replace="fragments :: header"></header>
<div class="navbar">
<div class="container">
<nav>
<ul class="meetup-nav">
<li>All Meetups
Create a Meetup
Delete a Meetup</li>
</ul>
</nav>
</div>
</div>
<section class="create-meetup-section">
<form method="post">
<div class="form-group">
<div class="form-wrapper">
<label class="form-name">Meetup Name:
<input th:field="${meetup.meetupName}" class="form-control">
</label>
<p class="error" th:errors="${meetup.meetupName}"></p>
<label class="form-email">Contact Email:
<input th:field="${meetup.meetupContactEmail}" class="form-control">
</label>
<p class="error" th:errors="${meetup.meetupContactEmail}"></p>
<!--label class="form-date">Date:
<input type="date" class="form-control">
</label-->
<label class="form-date">Date (mm/dd/yyy):
<input th:field="${meetup.meetupDate}" class="form-control">
</label>
<p class="error" th:errors="${meetup.meetupDate}"></p>
<label class="form-description">Description:
<textarea th:field="${meetup.meetupDescription}" class="form-control"></textarea>
</label>
<p class="error" th:errors="${meetup.meetupDescription}"></p>
<label class="form-category" th:for="category">Category:</label>
<select id="meetupCategory" name="meetupCategory">
<option value="Nature Walk">Nature Walk</option>
<option value="Cycling">Cycling</option>
<option value="Family Activity">Family Activity</option>
<option value="Athletic">Athletic</option>
</select>
<label class="form-trail" th:for="trail">Trail:</label>
<select name="trailId">
<option th:each="trail : ${trails}"
th:text="${trail.name}"
th:value="${trail.id}"></option>
</select>
</div>
</div>
<input type="submit" value="Create Meetup">
</div>
</form>
</section>
</body>
</html>
I believe create.html it's in the correct directory:
This is my controller class:
#RequestMapping("meetups")
public class MeetupController {
#Autowired
private MeetupRepository meetupRepository;
#Autowired
private TrailRepository trailRepository;
#Autowired
private AuthenticationController authenticationController;
#GetMapping
public String displayMeetups(Model model, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
User theUser = authenticationController.getUserFromSession(session);
model.addAttribute("theUser", theUser);
}
model.addAttribute("title", "Trail Meetups");
model.addAttribute("meetups", meetupRepository.findAll());
model.addAttribute("trails", trailRepository.findAll());
return "meetups/index";
}
#GetMapping("create")
public String displayCreateMeetupsForm(Model model) {
model.addAttribute("title", "Create A New Meetup");
model.addAttribute("trails", trailRepository.findAll());
model.addAttribute(new Meetup());
return "meetups/create";
}
#PostMapping("create")
public String processCreateMeetupsForm(#ModelAttribute #Valid Meetup newMeetup,
Errors errors, Model model, #RequestParam int trailId) {
if (errors.hasErrors()) {
model.addAttribute("title", "Create A New Meetup");
return "create";
}
Optional<Trail> trailObjs = trailRepository.findById(trailId);
if (trailObjs.isPresent()) {
Trail trail = trailObjs.get();
model.addAttribute("trail", trail);
model.addAttribute("trailId", trailId);
newMeetup.setTrail(trail);
meetupRepository.save(newMeetup);
model.addAttribute("meetups", meetupRepository.findAll());
return "meetups/index";
} else {
return "redirect:";
}
}
#GetMapping("delete")
public String displayDeleteMeetupsForm(Model model, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
User theUser = authenticationController.getUserFromSession(session);
model.addAttribute("theUser", theUser);
}
model.addAttribute("title", "Delete A Meetup");
model.addAttribute("meetups", meetupRepository.findAll());
return "meetups/delete";
}
#PostMapping("delete")
public String processDeleteMeetupsForm(#RequestParam(required = false) int[] meetupIds, Model model, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
User theUser = authenticationController.getUserFromSession(session);
model.addAttribute("theUser", theUser);
}
if (meetupIds != null) {
for (int id : meetupIds) {
meetupRepository.deleteById(id);
}
}
return "redirect:";
}
#GetMapping("details")
public String displayMeetupDetails(#RequestParam Integer meetupId, Model model, HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session != null) {
User theUser = authenticationController.getUserFromSession(session);
model.addAttribute("theUser", theUser);
}
Optional<Meetup> result = meetupRepository.findById(meetupId);
if (result.isEmpty()) {
model.addAttribute("title", "Invalid: A meetup with ID " + meetupId + "does not seem to exist.");
} else {
Meetup meetup = result.get();
model.addAttribute("title", meetup.getMeetupName() + " Details");
model.addAttribute("meetups", meetup);
model.addAttribute("trails", trailRepository.findAll());
}
return "meetups/details";
}

Related

Thymeleaf not loading content when I add validation in Spring Boot

Intro:
My app works in the general sense. All as I want. But when I set up validation (and it does work), it won't load all my hidden content. I moved it all to its own page, and it still did not work. So I am asking here with my original set up. I understand it is messy, but I will clean it up once I figure this out.
I can't find anything related to my problem, which makes me think, the way that I am doing it might not be the best approach. So pointers are more than welcome.
Problem:
Adding Validation to my forms breaks my Thymeleaf template. Unable to hide and unhide elements. It looks stuck. But on the terminal, the program does run fine.
My controller
(it's big... I still need to learn how to break it in smaller chunks.)
#Controller
#SessionAttributes({"guess", "word", "result", "level", "attempt", "message", "credits", "name", "gameScore"})
public class GameController {
private static final String WORD_TO_GUESS_CONSTANT = "WORD_TO_GUESS";
private static final String GUESSED_WORD_CONSTANT = "GUESSED_WORD";
private static final String RESULT_CONSTANT = "RESULT_WORD";
private static final String ATTEMPTS_CONSTANT = "ATTEMPTS";
private static final String TOTAL_ATTEMPTS_CONSTANT = "TOTAL_ATTEMPTS";
private static final String MESSAGE_CONSTANT = "MESSAGE";
private static final String CREDITS_CONSTANT = "CREDITS";
private static final String SELECTED_LEVEL_CONSTANT = "SELECTED_LEVEL";
private static final String NAME_CONSTANT = "NAME";
private static final String GAME_SCORE_CONSTANT = "GAME_SCORE";
private static final String SCORE_MULTIPLIER_CONSTANT = "SCORE_MULTIPLIER";
#Autowired
private RandomWordService randomWord;
#Autowired
private WordCheckService checkGuess;
#Autowired
private IsWordCorrectService isWordCorrectService;
#Autowired
private ScoreSavingService scoreSavingService;
#ModelAttribute("gameDto")
public GameDTO guessDTOForm() {
return new GameDTO();
}
#ModelAttribute("score")
public Score score() {
return new Score();
}
// GAME METHODS
#GetMapping(value = "/index")
public String home(Model model,
final HttpServletRequest request,
final HttpSession session,
GameDTO gameDTO,
Score score) {
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
model.addAttribute("lvlName", Level.values());
return "index";
}
#PostMapping(value = "/loadgame")
public String loadWord(
final HttpSession session, final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult,
Model model
) throws IOException {
if (bindingResult.hasErrors()) {
model.addAttribute("lvlName", Level.values());
model.addAttribute("name", session.getAttribute(NAME_CONSTANT));
model.addAttribute("levelSelected", session.getAttribute(SELECTED_LEVEL_CONSTANT));
model.addAttribute("attempt", session.getAttribute(ATTEMPTS_CONSTANT));
model.addAttribute("credits", session.getAttribute(CREDITS_CONSTANT));
model.addAttribute("attemptStart", session.getAttribute(TOTAL_ATTEMPTS_CONSTANT));
model.addAttribute("guess", session.getAttribute(GUESSED_WORD_CONSTANT));
model.addAttribute("result", session.getAttribute(RESULT_CONSTANT));
model.addAttribute("message", session.getAttribute(MESSAGE_CONSTANT));
model.addAttribute("gameScore", session.getAttribute(GAME_SCORE_CONSTANT));
return "index";
}
// NEW GAME
String word = (String) request.getSession().getAttribute(WORD_TO_GUESS_CONSTANT);
if (word == null) {
request.getSession().setAttribute(NAME_CONSTANT, gameDTO.getPlayerName());
request.getSession().setAttribute(ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
request.getSession().setAttribute(CREDITS_CONSTANT, gameDTO.getCredit());
request.getSession().setAttribute(SELECTED_LEVEL_CONSTANT, gameDTO.getLvlName().getLvlName());
request.getSession().setAttribute(TOTAL_ATTEMPTS_CONSTANT, gameDTO.getLvlName().getAttempts());
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameDTO.getScore());
request.getSession().setAttribute(SCORE_MULTIPLIER_CONSTANT, gameDTO.getLvlName().getMultiplier());
gameDTO.setWord((String) session.getAttribute(WORD_TO_GUESS_CONSTANT));
}
model.addAttribute("message", "");
return "redirect:/index";
}
#PostMapping(value = "/guess")
public String guessWord(
final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score,
#Valid GameDTO gameDTO, BindingResult bindingResult) throws IOException {
if (bindingResult.hasErrors()) {
return "index";
}
// variables
int attempts = (int) session.getAttribute(ATTEMPTS_CONSTANT);
int credits = (int) session.getAttribute(CREDITS_CONSTANT);
int startAttempts = (int) session.getAttribute(TOTAL_ATTEMPTS_CONSTANT);
String name = (String) session.getAttribute(NAME_CONSTANT);
// check word
String wordToGuess = (String) session.getAttribute(WORD_TO_GUESS_CONSTANT);
String guess = gameDTO.getGuess();
String result = checkGuess.resultWord(wordToGuess, guess);
String lvl = (String) session.getAttribute(SELECTED_LEVEL_CONSTANT);
// adjust score according to result
boolean wordIsCorrect = isWordCorrectService.isTheWordCorrect(result, wordToGuess);
int gameScore = (int) session.getAttribute(GAME_SCORE_CONSTANT);
int scoreMultiplier = (int) request.getSession().getAttribute(SCORE_MULTIPLIER_CONSTANT);
int wrongWord = gameDTO.getWrongWord();
int initialScore = gameDTO.getStartScore();
int finalScorePerWord = initialScore * scoreMultiplier;
// GAME LOGIC
if (!wordIsCorrect) {
String message = "";
message = "Wrong! Try again!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, --attempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore - wrongWord);
log(GameController.class, "Updated score: " + session.getAttribute(GAME_SCORE_CONSTANT));
if (attempts == 0) {
request.getSession().setAttribute(CREDITS_CONSTANT, --credits);
message = "Sorry, the word was: [ " + session.getAttribute(WORD_TO_GUESS_CONSTANT) + " ]";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, randomWord.selectRandomWord());
}
if (credits == 0) {
message = "Game over!";
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore);
// SAVE SCORE
score.setGameScore(gameScore);
score.setName(name);
score.setSelectedLevelName(lvl);
scoreSavingService.saveScore(score);
log(GameController.class, "Final score: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
} else {
String message = "Correct! Guess another word!";
wordToGuess = randomWord.selectRandomWord();
gameDTO.setWord(wordToGuess);
request.getSession().setAttribute(MESSAGE_CONSTANT, message);
request.getSession().setAttribute(WORD_TO_GUESS_CONSTANT, wordToGuess);
request.getSession().setAttribute(ATTEMPTS_CONSTANT, startAttempts);
request.getSession().setAttribute(GAME_SCORE_CONSTANT, gameScore + finalScorePerWord);
log(GameController.class, "Current score 2: " + session.getAttribute(GAME_SCORE_CONSTANT));
}
request.getSession().setAttribute(GUESSED_WORD_CONSTANT, guess);
request.getSession().setAttribute(RESULT_CONSTANT, result);
log(GameController.class, "Attempts are now: " + session.getAttribute(ATTEMPTS_CONSTANT));
return "redirect:/index";
}
// BUTTONS
#PostMapping(value = "/save")
public String giveUpAndSaveScore(final HttpSession session,
final HttpServletRequest request,
#ModelAttribute("score") Score score) {
score.setGameScore((Integer) session.getAttribute(GAME_SCORE_CONSTANT));
score.setName((String) session.getAttribute(NAME_CONSTANT));
score.setSelectedLevelName((String) session.getAttribute(SELECTED_LEVEL_CONSTANT));
scoreSavingService.saveScore(score);
request.getSession().invalidate();
return "index";
}
#GetMapping(value = "/scores")
public String seeScores(final HttpServletRequest request, Model model) {
List<Score> scoreList = scoreSavingService.getScore(5, 1);
model.addAttribute("score", scoreList);
return "scores";
}
// CLOSE SESSION
#PostMapping(value = "/destroy")
public String restartGame(final HttpServletRequest request) {
log(GameController.class, " Session closing. Removing the data.");
request.getSession().invalidate();
return "redirect:/index";
}
// EXCEPTION HANDLERS
#ExceptionHandler(value = ArrayIndexOutOfBoundsException.class)
public String handleArrayIndexOutOfBoundsException(final Model model) {
String text = "ERROR: Could not check empty <<guess>>.";
model.addAttribute("text", text);
return "ExceptionPage";
}
#ExceptionHandler(value = NullPointerException.class)
public String handleNullPointerException(final Model model) {
String text = "ERROR: Cannot compare words because <<word to guess>> is null";
model.addAttribute("text", text);
return "ExceptionPage";
}
}
Thymeleaf template for index.html
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-4.dtd">
<html lang="en" xmlns:th="www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>5Letters</title>
<link th:href="#{/bootstrap.min.css}" rel="stylesheet" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Quicksand&display=swap" rel="stylesheet">
</head>
<body>
<div class="container-sm">
<div class="col-xs-12" align="center">
<!-- HEADER -->
<img src="https://marisabel.nl/wp-content/uploads/2020/11/avatar-e1606738130828-150x150.png"
style="margin-top:2vh;">
<h1 style="margin-top:1vh; color:hotpink;"><b>5Letter Word Game</b></h1>
<h5>AKA: Lingo.</h5>
<hr>
</div>
<form method="get" th:action="#{/scores}">
<input type="submit" value="scores" class="btn btn-primary" />
</form>
<!-- Set Up Game Form -->
<div class="col-xs-12" align="center" th:hidden="${credits} != null">
<form th:action="#{/loadgame}" th:object="${gameDTO}" method="post">
<p>
<select class="form-group" th:field="*{lvlName}" id="dropDownList">
<option th:each="lvl : ${lvlName}" th:text="${lvl.lvlName}" th:value="${lvl.lvlName}">
</option>
</select>
</p>
<p><input type="text" class="form-group mx-sm-3 mb-2" id="name" th:field="*{playerName}"
th:placeholder="name" th:value="anonymous" /></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('playerName')}" th:errors="*{playerName}">
</p>
<p><input type="submit" value="start" class="btn btn-primary" /></p>
</form>
<p></p>
</div>
<!-- This whole session will load after name and level are chosen. Credits will be set to 3, triggering them to unhide -->
<div class="row">
<div class="col-xs-12 col-md-6" align="center" th:hidden="${credits} == null">
<!-- game information : always show after game setup -->
<h3 th:text="'Hello ' + ${name} + '!'"></h3>
<h3 th:text="'Level: '+ ${levelSelected}"></h3>
<h2 th:text="'Credits : '+ ${credits} + ' | Score: '+ ${gameScore}"></h2>
<h2 th:text="${attempt} + ' / ' + ${attemptStart}"></h2>
</div>
<div class="col-md-6" align="center">
<p>
<!-- Result messages and word after guessing -->
<h4 th:text="${message}"></h4>
<h2 id="result" th:text="${result}" th:hidden="${credits} == 0"></h2>
<!-- GUESS FORM -->
<form th:action="#{/guess}" th:object="${gameDTO}" method="post" th:hidden="${credits} == null">
<input id="guessField" type="text" th:field="*{guess}" placeholder="5letters" />
<p></p>
<p class="alert alert-danger" th:if="${#fields.hasErrors('guess')}" th:errors="*{guess}"></p>
<input type="submit" value="guess" th:disabled="${credits} == 0" class="btn btn-primary" />
</form>
</p>
</div>
</div>
<div class="row" style="margin-top:10vh;">
<div class="row justify-content-center" th:hidden="${credits} == null">
<div class="col col-lg-2 align-items-center">
<!-- Destroy session data and go to index -->
<form method="post" th:action="#{/destroy}">
<input type="submit" value="play again" class="btn btn-danger" />
</form>
<p></p>
</div>
<div class="col-md-auto align-items-center" width="50%">
<!-- Display last typed word -->
<h4 th:text="'Your guess was:'" th:hidden="${attempt} == ${attemptStart}"></h4>
<h2 id="guess" th:text="${guess}" th:hidden="${attempt} == ${attemptStart}"></h2>
<p></p>
</div>
<div class="col col-lg-2 align-items-center">
<!-- Stops the game if you are bored. Usually needed with EASY mode. -->
<form method="post" th:action="#{/save}">
<input type="submit" value="i'm tired" class="btn btn-danger" />
<p></p>
</form>
</div>
</div>
</div>
</body>
</html>
I moved the content I was hiding to its own page. It loads the page, but the content remains hidden. Even after taking a break I am still unable to find what is wrong. Specially when it works 100% without validation.

Spring tags form do not show validation errors in jsp

I am writing a page with user registration. Faced a display problem in the form of validation errors
My Controller:
#Controller
public class UIController {
private final Logger log = LoggerFactory.getLogger(getClass());
#Autowired
private UserService service;
#Autowired
private SecurityService securityService;
#Autowired
private UserValidator validator;
#PostMapping(value = "/login")
public String signIn(#ModelAttribute("userForm") UserTo userForm, BindingResult bindingResult, Model model) {
validator.validate(userForm, bindingResult);
User user = service.findByLogin(UserUtil.createNewFromTo(userForm).getLogin());
if (!userForm.getPassword().equals(user.getPassword())) {
log.info("invalid password {}", user);
return "redirect:/login";
}
log.info("signIn {}", user);
securityService.autologin(user.getLogin(), user.getPassword());
return "redirect:/welcome";
}
}
My Validator:
#Component
public class UserValidator implements Validator {
#Autowired
private UserRepositoryImpl userRepository;
#Override
public boolean supports(Class<?> aClass) {
return UserTo.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
UserTo user = (UserTo) o;
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "login", "NotEmpty");
if (user.getLogin().length() < 6 || user.getLogin().length() > 32) {
errors.rejectValue("login", "Size.userForm.login");
}
if (userRepository.findByLogin(user.getLogin()) != null) {
errors.rejectValue("login", "Duplicate.userForm.login");
}
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty");
if (user.getPassword().length() < 8 || user.getPassword().length() > 32) {
errors.rejectValue("password", "Size.userForm.password");
}
}
}
My jsp form :
<div id="wrapper">
<!--SLIDE-IN ICONS-->
<div class="user-icon"></div>
<div class="pass-icon"></div>
<!--END SLIDE-IN ICONS-->
<!--LOGIN FORM-->
<form:form name="login-form" modelAttribute="userForm" class="login-form" method="post" id="form">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
<!--CONTENT-->
<div class="content">
<!--USERNAME-->
<spring:bind path="login">
<div class="form-group ${status.error ? 'has-error' : ''}">
<form:input name="login" id="login" type="text" path="login" class="input username"
placeholder="Login"></form:input>
<form:errors path="login"></form:errors>
</div>
</spring:bind>
<!--END USERNAME-->
</div>
<!--END CONTENT-->
<!--FOOTER-->
<div class="footer">
<button class="button" type="submit">Login</button>
<button class="register" type="button" name="submit" onclick=" register_url('${contextPath}/registration')">
Register
</button>
</div>
<!--END FOOTER-->
</form:form>
</div>
When debugging in chrome when sending a POST request, a 302 HTTP error appears.
Accordingly, if I set a breakpoint in the controller, then the debug is not processed.
Tell me what could be the problem?
The "form" tag in your jsp does not have "action" attribute.
You should not redirect on errors, because the redirect will erase the binding result. Instead do :
if (!userForm.getPassword().equals(user.getPassword())) {
log.info("invalid password {}", user);
return "login";
}

Spring #ModelAttributes auto binding

Hi i am tring to use #ModelAttributes for auto binding from view to controller.
this jsp page should send all elements which is Personal Schedule VO.
<form name="insertFrm" action="myScheduleInsert" method="POST">
<ul>
<input type="hidden" class="form-control" name="perschd_num" >
<li>
<label class='control-label'>title</label>
<input type="text" class="form-control" name="perschd_title" >
</li>
<li>
<input type="hidden" class="form-control" name="perschd_writer" >
<li>
<label class='control-label'>start date</label>
<input type="date" id="fullYear" class="form-control" name="perschd_start_date" value="">
</li>
<li>
<label class='control-label'>end date</label>
<input type="date" class="form-control" name="perschd_end_date" >
</li>
<li>
<label class='control-label'>content</label>
<input type="text" class="form-control" name="perschd_cont" >
</li>
</ul>
</div>
<!-- button-->
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<input type="submit" class="btn btn-primary">
</form>
and this is my controller for insert feature by using #ModelAttributes PerschdVO perschd and when i try print all out, all is null.
//insertSchedule
#RequestMapping("/myScheduleInsert")
public String myScheduleInsert(#ModelAttribute PerschdVO perschdVO ){
String view="redirect:/professor/mypage/mySchedule";
*System.out.println(perschdVO);*
try {
proService.insertPerschd(perschdVO);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return view;
}
and this is PerschdVO
public class PerschdVO {
private int perschd_num;
private String perschd_title;
private String perschd_cont;
private Date perschd_date;
private String perschd_start_date;
private String perschd_end_date;
private String perschd_writer; //id
public String getPerschd_writer() {
return perschd_writer;
}
public void setPerschd_writer(String perschd_writer) {
this.perschd_writer = perschd_writer;
}
public int getPerschd_num() {
return perschd_num;
}
public void setPerschd_num(int perschd_num) {
this.perschd_num = perschd_num;
}
public String getPerschd_title() {
return perschd_title;
}
public void setPerschd_title(String perschd_title) {
this.perschd_title = perschd_title;
}
public String getPerschd_cont() {
return perschd_cont;
}
public void setPerschd_cont(String perschd_cont) {
this.perschd_cont = perschd_cont;
}
public Date getPerschd_date() {
return perschd_date;
}
public void setPerschd_date(Date perschd_date) {
this.perschd_date = perschd_date;
}
public String getPerschd_start_date() {
return perschd_start_date;
}
public void setPerschd_start_date(String perschd_start_date) {
this.perschd_start_date = perschd_start_date;
}
public String getPerschd_end_date() {
return perschd_end_date;
}
public void setPerschd_end_date(String perschd_end_date) {
this.perschd_end_date = perschd_end_date;
}
why this elements from jsp does not match with this? Eventhough i matched all name in jsp file to PerschdVO's get() names.
and if i fix like this
public String myScheduleInsert(
Principal who,
#RequestParam("perschd_start_date")String perschd_start_date,
#RequestParam("perschd_end_date")String perschd_end_date,
#RequestParam("perschd_title")String perschd_title,
#RequestParam("perschd_cont")String perschd_cont
){
PerschdVO perschdVO = new PerschdVO();
...}
than the data from jsp could be sent to #Controller, but still can't get informations about using #ModelAttributes. cos if i use that always 400 bad request happened.

Can't get selected option in controller

I have a form to list up buckets in select options, it uploads file to bucket after select bucket and file, but I can't get selected bucket in my controller, The folder alwsy return empty string, though mutipartFile is no problem, I really want to know why!
I googled for all this week but no result what I need!
I am very new in thymeleaf even in spring framework:(
Pls help me to solve this simple problem to you:)
part of html file as below:
<form role="form" enctype="multipart/form-data" action="#" th:object="${folder}" th:action="#{'/drill/skin/upload'}" method="POST">
<div class="form-group">
<label class="form-control-static">Select Bucket</label>
<select class="form-control" th:field="${folder}">
<option th:each="bucket : ${buckets}" th:value="${bucket.name}" th:text="${bucket.name}">bucket</option>
</select>
</div>
<label class="form-control-static" for="inputSuccess">Select Upload File</label>
<div class="form-group">
<input type="file" class="form-control" name="uploadFile"/>
</div>
<div class="form-group">
<button class="btn btn-primary center-block" type="submit">Upload</button>
</div>
</form>
controller as below:
#RequestMapping(value="/", method=RequestMethod.GET)
public String provideUploadInfo(Model model) {
List<Bucket> buckets = s3Service.listBuckets();
model.addAttribute("buckets", buckets);
model.addAttribute("folder", "com.smartstudy");
return "index";
}
#RequestMapping(value="/upload", method=RequestMethod.POST)
public String handleFileUpload(
#ModelAttribute("folder") String folder,
#RequestParam("uploadFile") MultipartFile uploadFile, Model model) {
log.info("Bucket: " + folder + ", uploadFile: " + uploadFile.getOriginalFilename());
if (!uploadFile.isEmpty() && !folder.isEmpty()) {
return s3Service.upload(uploadFile, folder);
}
return "index";
}
There are couple of issues in your code.
Ideally, the whole form should be encapsulated in one form-backing object. In your case, create a Java object that wraps the folder and the file together.
class BucketFileForm{
private MultipartFile uploadFile;
private String folder;
public String getFolder() {
return folder;
}
public void setFolder(String folder) {
this.folder = folder;
}
public MultipartFile getUploadFile() {
return uploadFile;
}
public void setUploadFile(MultipartFile uploadFile) {
this.uploadFile = uploadFile;
}
}
Make this object available in the model so that you can access it in the view
#RequestMapping(value="/", method=RequestMethod.GET)
public String provideUploadInfo(Model model) {
List<Bucket> buckets = s3Service.listBuckets();
model.addAttribute("buckets", buckets);
//replace this
//model.addAttribute("folder", "com.smartstudy");
//with
BucketFileForm bucketFileForm = new BucketFileForm();
bucketFileForm.setFolder("com.smartstudy");
model.addAttribute("bucketFileForm", bucketFileForm);
return "index";
}
Now, use this form-backing object in the form
<form role="form" enctype="multipart/form-data" action="#" th:object="${bucketFileForm}" th:action="#{'/drill/skin/upload'}" method="POST">
<div class="form-group">
<label class="form-control-static">Select Bucket</label>
<select class="form-control" th:field="*{folder}">
<option th:each="bucket : ${buckets}" th:value="${bucket.name}" th:text="${bucket.name}">bucket</option>
</select>
</div>
<label class="form-control-static" for="inputSuccess">Select Upload File</label>
<div class="form-group">
<input type="file" th:field="*{uploadFile}" class="form-control" name="uploadFile"/>
</div>
<div class="form-group">
<button class="btn btn-primary center-block" type="submit">Upload</button>
</div>
</form>
Then modify your POST endpoint to accomodate these changes.
#RequestMapping(value="/upload", method=RequestMethod.POST)
public String handleFileUpload(#ModelAttribute("bucketFileForm") final BucketFileForm form, final BindingResult bindingResult, Model model) {
log.info("Bucket: " + form.getFolder() + ", uploadFile: " + form.getUploadFile().getOriginalFilename());
if (!form.getUploadFile().isEmpty() && !form.getFolder().isEmpty()) {
return s3Service.upload(uploadFile, folder);
}
return "index";
}

Spring Boot multiple controllers with same mapping

My problem is very similar with this one: Spring MVC Multiple Controllers with same #RequestMapping
I'm building simple Human Resources web application with Spring Boot. I have a list of jobs and individual url for each job:
localhost:8080/jobs/1
This page contains job posting details and a form which unauthenticated users -applicants, in this case- can use to apply this job. Authenticated users -HR Manager-, can see only posting details, not the form. I have trouble with validating form inputs.
What I tried first:
#Controller
public class ApplicationController {
private final AppService appService;
#Autowired
public ApplicationController(AppService appService) {
this.appService = appService;
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.POST)
public String handleApplyForm(#PathVariable Long id, #Valid #ModelAttribute("form") ApplyForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "job_detail"; //HTML page which contains job details and the application form
}
appService.apply(form, id);
return "redirect:/jobs";
}
#RequestMapping(value = "/applications/{id}", method = RequestMethod.GET)
public ModelAndView getApplicationPage(#PathVariable Long id) {
if (null == appService.getAppById(id)) {
throw new NoSuchElementException(String.format("Application=%s not found", id));
} else {
return new ModelAndView("application_detail", "app", appService.getAppById(id));
}
}
}
As you guess this didn't work because I couldn't get the models. So I put handleApplyForm() to JobController and changed a little bit:
#Controller
public class JobController {
private final JobService jobService;
private final AppService appService;
#Autowired
public JobController(JobService jobService, AppService appService) {
this.jobService = jobService;
this.appService = appService;
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.POST)
public ModelAndView handleApplyForm(#PathVariable Long id, #Valid #ModelAttribute("form") ApplyForm form, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return getJobPage(id);
}
appService.apply(form, id);
return new ModelAndView("redirect:/jobs");
}
#RequestMapping(value = "/jobs/{id}", method = RequestMethod.GET)
public ModelAndView getJobPage(#PathVariable Long id) {
Map<String, Object> model = new HashMap<String, Object>();
if (null == jobService.getJobById(id)) {
throw new NoSuchElementException(String.format("Job=%s not found", id));
} else {
model.put("job", jobService.getJobById(id));
model.put("form", new ApplyForm());
}
return new ModelAndView("job_detail", model);
}
}
With this way, validations works but I still can't get the same effect here as it refreshes the page so that all valid inputs disappear and error messages don't appear.
By the way, job_detail.html is like this:
<h1>Job Details</h1>
<p th:inline="text"><strong>Title:</strong> [[${job.title}]]</p>
<p th:inline="text"><strong>Description:</strong> [[${job.description}]]</p>
<p th:inline="text"><strong>Number of people to hire:</strong> [[${job.numPeopleToHire}]]</p>
<p th:inline="text"><strong>Last application date:</strong> [[${job.lastDate}]]</p>
<div sec:authorize="isAuthenticated()">
<form th:action="#{/jobs/} + ${job.id}" method="post">
<input type="submit" value="Delete this posting" name="delete" />
</form>
</div>
<div sec:authorize="isAnonymous()">
<h1>Application Form</h1>
<form action="#" th:action="#{/jobs/} + ${job.id}" method="post">
<div>
<label>First name</label>
<input type="text" name="firstName" th:value="${form.firstName}" />
<td th:if="${#fields.hasErrors('form.firstName')}" th:errors="${form.firstName}"></td>
</div>
<!-- and other input fields -->
<input type="submit" value="Submit" name="apply" /> <input type="reset" value="Reset" />
</form>
</div>
Check thymeleaf documentation here
Values for th:field attributes must be selection expressions (*{...}),
Also ApplyForm is exposed then you can catch it in the form.
Then your form should looks like this:
<form action="#" th:action="#{/jobs/} + ${job.id}" th:object="${applyForm}" method="post">
<div>
<label>First name</label>
<input type="text" name="firstName" th:value="*{firstName}" />
<td th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"></td>
</div>
<!-- and other input fields -->
<input type="submit" value="Submit" name="apply" /> <input type="reset" value="Reset" />
</form>

Resources