I have this annotation at the top of my controller:
#SessionAttributes("user")
And this mapping:
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(ModelMap model){
model.clear();
But when I navigate to that URL it's still able to retrieve the User session attributes..
How do I properly clear the ModelMap value?
Looks like I need this signature instead w/ SessionStatus:
#RequestMapping(value="/logout", method = RequestMethod.GET)
public String logout(SessionStatus status){
status.setComplete();
return "redirect:/";
}
Related
#Controller
#RequestMapping("/")
#Validated
public class AddressWebController {
#GetMapping(value = {"/{id}")
public String editForm(#PathVariable(value = "id") #Positive Long id, Model model) {
// process
}
#PutMapping(value = {"/edit"})
public String editProcess(#ModelAttribute #Valid Form form,
BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// ???
}
// process
}
}
public class Form {
#NotBlank
private String name;
}
curl -XGET 'http://localhost/5'
is pass
curl -XGET 'http://localhost/-5'
A ConstraintViolationException exception is thrown. Due to #Validated and #Positive
curl 'http://localhost/edit' --data-raw '_method=PUT&name='
I expected it to come in as bindingResult.hasErrors() in that part, but the same ConstraintViolationException is thrown.
When #Validated is removed, bindingResult.hasErrors() works, but #Positive annotation does not.
How can I use #Validated annotation and BindingResult together?
What's the best approach to avoid repeating the same userService DB lookup over and over again in my controller methods?
I'm using Spring Boot 1.5.2 with spring-boot-starter-security and spring-boot-starter-thymeleaf for templating.
I tried adding an instance variable for SecurityContextHolder.getContext().getAuthentication() but it gave me a NullPointerException.
#Controller
public class DashboardController {
#Autowired
private UserService userService;
#Value("${product.name}")
private String productName;
#RequestMapping(value="/dashboard", method = RequestMethod.GET)
public ModelAndView home() {
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("email", user.getEmail());
modelAndView.setViewName("dashboard");
return modelAndView;
}
#RequestMapping(value="/dashboard/faq", method = RequestMethod.GET)
public ModelAndView faq(){
ModelAndView modelAndView = new ModelAndView();
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = userService.findUserByEmail(auth.getName());
modelAndView.addObject("email", user.getEmail());
modelAndView.addObject("productname", productName);
modelAndView.setViewName("faq");
return modelAndView;
}
If you want to get at the user that is stored in the session, you can use this annotation:
#RequestMapping("/me")
public User me(#AuthenticationPrincipal User user) {
return user;
}
If you then want the user to always be available in thymeleaf I would use a #ControllerAdvice
#ControllerAdvice(annotations = Controller.class)
public class GlobalVariablesControllerAdvice {
#ModelAttribute("user")
public User user() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
User user = null;
// get user from authentication, but make sure to check for nulls
return user;
}
}
I am new to Thymeleaf and am trying to check for a null attribute in my template
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
The form submits fine if I am editing an existing user with an id already defined, however using the same form to add a new user I get the following
HTTP Status 400 - http://localhost:8080/myApp/saveUser/null"
My controller:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
My thought is if I can check for the null id I can plug in a unique one somehow. Better yet, if I could make use of the #GeneratedValue set on my User object's ID then I think I'd be in good shape
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
EDIT Including my user edit form method which returns the same form but pre-populated with the User's model Attributes
#RequestMapping(value = "/edit/user/{id}", method = RequestMethod.GET)
public ModelAndView getUserEditForm(#PathVariable Long id, Model model){
ModelAndView mav = new ModelAndView("userForm");
User user = userRepo.findOne(id);
mav.addObject("userForm", user);
return mav;
}
EDIT 2 Including my whole form (ID is "hidden" from user)
<form th:action="#{/saveUser/__${user.id}__}" th:object="${user}" method="post">
<input id="id" type="hidden" th:field="*{id}" />
<input id="id" type="text" th:field="*{name}" />
<input id="id" type="text" th:field="*{address}" />
<button id="save">Save</button>
</form>
As per discussion assuming that the following method is the one you call which should populate the user object and thus form fails on submission:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
model.addAttribute("user", user);
userRepo.save(user); //JPA Repo
return "success";
}
The reason that method doesn't work is because you are potentially passing an empty user object to begin with.
To remediate this you need to implement checks to ensure object is not null prior to calling the page.
one solution could be:
#RequestMapping(value = "/saveUser/{id}", method = RequestMethod.POST)
public String saveUser(#ModelAttribute("user") User user, #PathVariable Long id, Model model) {
userRepo.save(user); //JPA Repo
if(user == null) // check if user object is empty
user = new User(); // if user is empty, then instantiate a new user object
model.addAttribute("user", user);
return "success";
}
The above will ensure that when you passing user object to the model, it is always available.
I've temporarily resolved this by creating a new #Controller to accept "null" at the end of the #RequestMapping (which kind of makes sense when creating a new user with a dynamic ID?), and just reimplementing the same logic. So when this is called
http://localhost:8080/myApp/saveUser/null"
It is mapped to my new Controller
#RequestMapping(value = "/saveUser/null", method = RequestMethod.GET)
public ModelAndView saveUser(#ModelAttribute("user") User user, Model model){
model.addAttribute("user", user);
userRepo.save(user);
return "success";
}
I use Spring, jsp and Hibernate in my project. I have two entities Employee and Department. Dapartment is a part of Employee and they have relationship one-to-many
#Entity
#Table(name = "employee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToOne
#JoinColumn(name = "dep_id")
private Department department;
I create controller, DAO and jsp pages for view.
My problem: I want to update data of Employee in my jsp page. Before this I add Employee and list of departments in model
In controller:
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
In JSP:
<form method="post">
<select value="${employee.department}">
<c:forEach items="${departments}" var ="dep">
<option value="${dep}">${dep.name}</option>
</c:forEach>
</form>
In controller (post request)
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String updateEmployee(#PathVariable("id") long id, Employee employee) {
employee.setId(id);
employeeDao.update(employee);
return "redirect:/employees";
}
but value employee.department=null Why?
Of course, on jsp page in "select" tag I can create variable dep I mean:
<select name ="dep">
<option value="${dep.id}">${dep.name}</option>
</select>
and then in controller using id of department i will be able to get department from database and update Employee. Is it right way?
You have not posted your full Controller however if we assume the initial get request looks like:
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ModelAndView loadForEdit(#PathVariable("id") long id)
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
return new ModelAndView(...);
}
Then on loadForEdit() we load an Employee and Departments for edit and set them in the model for rendering the edit page.
Now, on submit, the POST method updateEmployee(...) knows nothing about this previous Employee and Department and therefore the framework is simply passing in a new instance to updateEmployee(...).
If you refactor along the lines of the following, then on the call to both the GET and POST handlers, the method annotated with #ModelAttribute(value = "employee") will execute. In the first case it will be added to the model, as previously, and in the second case the Employee will be retrieved, the fields bound to the updated values amd will then be passed to your POST handler.
#RequestMapping(value = "/{id}", method = RequestMethod.Get)
public String loadForEdit(){
return "nextView";
}
#RequestMapping(method = RequestMethod.POST)
public String updateEmployee(value = "/{id}", #ModelAttribute("employee") Employee employee)
{
return "redirect:/employees";
}
#ModelAttribute(value = "employee")
public Employee getEmployee(#PathVariable("id") long id)
{
return employeeDao.find(id);
}
#ModelAttribute(value = "departments")
public List<Department> getDepartments()
{
return departmentDao.list());
}
There is a lot of flexibility on how you can handle this:
See 'Using #ModelAttribute on a method argument' in:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/mvc.html
#RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method =
> RequestMethod.POST) public String processSubmit(#ModelAttribute Pet
> pet) { }
Given the above example where can the Pet instance come from?
There are several options:
It may already be in the model due to use of #SessionAttributes — see
the section called “Using #SessionAttributes to store model attributes
in the HTTP session between requests”.
It may already be in the model
due to an #ModelAttribute method in the same controller — as explained
in the previous section. [My suggestion]
It may be retrieved based on a URI template
variable and type converter (explained in more detail below).
It may be instantiated using its default constructor. [current position]
Sory, I forgot to publish the decision to solve. Thanks to all who helped me to find right solution.
In controller:
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public String getEmployee(#PathVariable("id") long id, Model model) {
model.addAttribute("employee", employeeDao.find(id));
model.addAttribute("departments", departmentDao.list());
return "employees/view";
}
Then we need to edit view. On JSP-page:
<%# taglib prefix="sf" uri="http://www.springframework.org/tags/form"%>
<sf:form modelAttribute="employee" method="post">
<sf:select path="department" id="select-departments" >
<c:forEach items="${departments}" var="dep" varStatus="status">
<option value="${dep.id}">${dep.name}</option>
</c:forEach>
</sf:select>
</sf:form>
We also need to create department's editor:
public class DepartmentEditor extends PropertyEditorSupport {
private DepartmentDao departmentDao;
public DepartmentEditor(DepartmentDao departmentDao) {
this.departmentDao = departmentDao;
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
long id = Long.parseLong(text);
Department department = departmentDao.find(id);
setValue(department);
}
}
And at the and we need to add some code in controller:
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(Department.class, new DepartmentEditor(departmentDao));
}
#RequestMapping(value = "/{id}", method = RequestMethod.POST)
public String updateEmployee(#PathVariable("id") long id, #ModelAttribute Employee employee) {
employee.setId(id);
employeeDao.update(employee);
return "redirect:/employees";
}
I have a web application in SpringMVC, After login I want to point it to "/" but its always pointing me to /home.
<security:form-login
login-page="/home/login"
default-target-url="/"
always-use-default-target="true"
authentication-failure-url="/auth/failed"/>
here is homecontroller.java
#Controller
#RequestMapping(value = "/")
#SessionAttributes({"loginModel"})
public class HomeController {
#Autowired
private LoginModelService loginModelService;
#RequestMapping(method = RequestMethod.GET)
public String loadHome(Model model, Principal principal) {
model.addAttribute("loginModel", loginModelService.getLoginModelByUserLoginName(principal.getName()));
return "index";
}
#RequestMapping(method = RequestMethod.GET, value = "/home")
public String showHome(Model model, Principal principal) {
model.addAttribute("loginModel", loginModelService.getLoginModelByUserLoginName(principal.getName()));
return "system/home";
}
}
After login showHome method is being called instead of loadHome