I have a spring controller that I want a method to handle a certain request and then redirect to another one with keeping some value attached, so I will use RedirectAttributes on the first one and #ModalAttribute on the second, but the thing is I will not always have this modal attribute existing so I want to add it only if it exists.
#RequestMapping("/main")
public String getMain(Model model,HttpSession session,#ModalAttribute List<Loans> loansList){
if(session.getAttribute("user") != null){
if(session.getAttribute("current_start")!=null){
model.addAttribute("loans",loanDao.findAll((Integer) session.getAttribute("current_start")));
} else {
model.addAttribute("loans",loanDao.findAll(0));
session.setAttribute("current_start",0);
}
model.addAttribute("loan",new Loan());
model.addAttribute("countries",countryDao.findAll());
model.addAttribute("types",typeDao.findAll());
session.setAttribute("total_loans_number", loanDao.findCount());
return "main";
} else {
return "redirect:index";
}
}
and the redirecting one one is
#RequestMapping(value = "/search")
public String searchLoans(Model model,RedirectAttributes redirectAttributes,
#RequestParam String keyword){
redirectAttributes.addAttribute("loansList",loanDao.findAll(keyword));
return "redirect:/main";
}
but here the #ModalAttribute fails because it sometimes does not exist,sometimes I request main with out the loansList, how to make a condition to add it only if it exists ? or how to do this correctly ?
you can let spring populate your model attributes using #ModalAttribute annotation on methods:
#ModalAttribute("results")
public List<Loans> populateLoans() {
return new ArrayList<Loans>();
}
#RequestMapping("/main")
public String getMain(Model model,HttpSession session,#ModalAttribute("results") List<Loans> loansList){
if (CollectionUtils.isNotEmpty(loanList)) {
// do something if the loan list is not empty.
}
}
Related
I have been studying spring boot for a few weeks.
I am building a simple api using hibernate + jpa with a mysql database.
I have a resource call TvShowReminderResponseDTO :
public class TvShowReminderResponseDTO {
// Attributes
private Integer idTvShowReminder;
private User user;
private UserTvShow userTvShow;
private TvShowDetailsResponseDTO tvShowDetailsResponseDTO;
private Boolean completed;
private Integer currentSeason;
private Integer currentEpisode;
private Integer personalRating;
// rest of the code omittedfor brevity
}
In my controller i have a basic update PATCH endpoint, that receives the id of the tv show reminder (entity) that is stored in my database and also i receive a TvShowReminderPatchDTO with the information i want to update:
PatchDTO and Controller:
public class TvShowReminderPatchDTO {
// Attributes
private Optional<Boolean> completed;
private Optional<Integer> currentSeason;
private Optional<Integer> currentEpisode;
private Optional<Integer> personalRating;
// rest of the code omittedfor brevity
}
#PatchMapping("/{idTvShowReminder}")
public void updateTvShowReminder(#RequestBody #Valid TvShowReminderPatchDTO tvShowReminderToUpdate,
#PathVariable Integer idTvShowReminder){
tvShowReminderService.updateTvShowReminder(tvShowReminderToUpdate,idTvShowReminder);
}
Also I have my service method that is in charge of searching the TvShowReminder entity by its id, and then update the information we get from the client.
public void updateTvShowReminder(TvShowReminderPatchDTO tvShowReminderToUpdate, Integer idTvShowReminder) {
Optional<TvShowReminder> tvShowReminder = getTvShowReminder(idTvShowReminder);
TvShowReminder currentTvShowReminder = tvShowReminder.get();
if(tvShowReminderToUpdate.getCompleted() != null) {
if (tvShowReminderToUpdate.getCompleted().isPresent()) {
currentTvShowReminder.setCompleted(tvShowReminderToUpdate.getCompleted().get());
} else {
currentTvShowReminder.setCompleted(null);
}
}
if(tvShowReminderToUpdate.getCurrentSeason() != null) {
if (tvShowReminderToUpdate.getCurrentSeason().isPresent()) {
currentTvShowReminder.setCurrentSeason(tvShowReminderToUpdate.getCurrentSeason().get());
} else {
currentTvShowReminder.setCurrentSeason(null);
}
}
if(tvShowReminderToUpdate.getCurrentEpisode() != null) {
if (tvShowReminderToUpdate.getCurrentEpisode().isPresent()) {
currentTvShowReminder.setCurrentEpisode(tvShowReminderToUpdate.getCurrentEpisode().get());
} else {
currentTvShowReminder.setCurrentEpisode(null);
}
}
if(tvShowReminderToUpdate.getPersonalRating() != null) {
if (tvShowReminderToUpdate.getPersonalRating().isPresent()) {
currentTvShowReminder.setPersonalRating(tvShowReminderToUpdate.getPersonalRating().get());
} else {
currentTvShowReminder.setPersonalRating(null);
}
}
tvShowReminderRepository.save(currentTvShowReminder);
}
I have a question about the #valid annotation in the controller: i thought that it will check if the object that we send from postman for example is of type TvShowReminderPatchDTO , but i can send an entire different object and the controller will start its excecution, and the TvShowReminderPatchDTO will have all its attributes in NULL.
Whats the best way to check if the request body its in fact a TvShowReminderPatchDTO ?
I want to validate if the object we get from the Request is an instance of the TvShowReminderPatchDTO, and if not, throw an Exception.
The method that is doing the PATCH is working but its very ugly, I use optional as attributes in the TvShowReminderPatchDTO , so i can distinguish if the client wants to set a NULL (send an attribute with a null value ) or if the attribute was ommited (it does not appear on the request body) so we dont need to do anything, meaning we dont update it.
Can you guys recommend a better way to do this or improve the existing code?
Add some required fields using #NotNull annotation in your dto to help Spring understand which attributes should be present in your type
Don't use Optional. There is already JsonNullable for this purpose
public class TvShowReminderPatchDTO
{
#NotNull
private JsonNullable<Boolean> completed = JsonNullable.undefined();
}
And in controller method:
if (dto.getCompleted().isPresent()) {
object.setCompleted(dto.getCompleted().get());
}
That's it, no null-checks required, just set the value
I am new to the Java Play Framework and I'm trying to get the authentication to work. So I am following this tutorial: https://www.playframework.com/documentation/2.1.0/JavaGuide4
Here is my code:
public static Result authenticate()
{
Form<Login> loginForm = form(Login.class).bindFromRequest();
return ok(loginForm.toString());
}
public static class Login
{
public String email;
public String password;
public String validate()
{
return "VALIDATE "+email+password;
}
}
In the method autheticate() I can see the submitted values of the form, but the method validate() in the Login class does not see them (the variables are always null).. The output of loginForm.toString() contains:
Form(of=class controllers.Application$Login, data={email=asdf#asdf, password=asdf}, value=None, errors={=[ValidationError(,[VALIDATE nullnull],[])]})
As you can see, the data is received.. But in the validate method the data suddenly is equal to null. So how do I fix this?
You don't mention how you are calling validate() however I think this might do the trick, do something along the lines of:
public static Result authenticate() {
Form<Login> form = form(Login.class).bindFromRequest();
// handle errors
if (!form.hasErrors()) {
Login login = form.get();
Logger.debug(login.validate());
} else {
// bad request
}
}
This works for me.
Method validate in your model should return null if you think that validation has passed, otherwise you should return error message text. Then you need to check form if it contains error by "hasGlobalError" method. globalError is filled when validate() method returns String instead of null. But in your case you should use some model field annotations - https://www.playframework.com/documentation/2.3.x/api/java/play/data/validation/Constraints.html.
If you want to check if form fails on those - then you use "hasErrors" method.
public static class Login {
#Constraints.Email
public String email;
#Constraints.MinLength(value = 6)
public String password;
}
Such model will check if provided emails is really email and if password is longer or equal 6 characters.
ps. Do not use toString on template, you should use render()
Good day everybody.
Please help me.
I have application and simple Controller which search data from database and when data founded in database it render them in browser also when data appears on page there is also Render in EXCEL button appears if user wants render it in Excel file. Here is controller:
#Scope("session")
#Controller
public class SearchController {
//Apache log4j logger for this controller to bring info to console of executing inside of this controller
#SuppressWarnings("unused")
private static final Logger logger = LoggerFactory.getLogger(SearchController.class);
#Autowired
private EducationWebServiceInterface educationWebService;
List<FormDate> listForm = null;
#ModelAttribute("listOfDates")
public List<Date> prepareModelDate() {
List<Date> listOfDates = educationWebService.fetchAllDatesService();
return listOfDates;
}
#ModelAttribute("listOfNames")
public List<String> prepareModelNames() {
List<String> listOfNames = educationWebService.fetchAllInstitutionNamesService();
return listOfNames;
}
#ModelAttribute("listOfTypes")
public List<String> prepareModelTypes() {
List<String> listOfTypes = educationWebService.fetchAllInstitutionTypesService();
return listOfTypes;
}
#RequestMapping(value="/search", method=RequestMethod.GET)
public String search(FormBackingObjectSearch fbos, Model model) {
model.addAttribute("fbosAttributes", fbos);
return "search";
}
#RequestMapping(value="/result", method=RequestMethod.GET)
public String resultHTML(#RequestParam String particularDate,
#RequestParam String nameOfInstitution,
#RequestParam String typeOfInstitution,
#ModelAttribute("fbosAttributes") #Validated FormBackingObjectSearch fbos,
BindingResult bindingResult,
Model model) throws Exception {
ValidatorSearch validatorSearch = new ValidatorSearch();
validatorSearch.validate(fbos, bindingResult);
if(bindingResult.hasErrors()) {
return "search";
}
listForm = new ArrayList<FormDate>();
//Case 1:
if(!fbos.getParticularDate().equals("") && !fbos.getNameOfInstitution().equals("") && fbos.getTypeOfInstitution().equals("")) {
listForm = educationWebService.fetchByDateAndNameService(DateChangerUtils.dateConvertation(fbos.getParticularDate()), fbos.getNameOfInstitution());
model.addAttribute("findAttributes", listForm);
//Case 2:
} else if(!fbos.getParticularDate().equals("") && fbos.getNameOfInstitution().equals("") && !fbos.getTypeOfInstitution().equals("")) {
listForm = educationWebService.fetchByDateAndTypeService(DateChangerUtils.dateConvertation(fbos.getParticularDate()), fbos.getTypeOfInstitution());
model.addAttribute("findAttributes", listForm);
//Case 3:
} else if(!fbos.getParticularDate().equals("") && fbos.getNameOfInstitution().equals("") && fbos.getTypeOfInstitution().equals("")) {
listForm = educationWebService.fetchByDateService(DateChangerUtils.dateConvertation(fbos.getParticularDate()));
model.addAttribute("findAttributes", listForm);
//Case 4:
} else {
throw new Exception("Exception occurs because it's not correspond to any case in controller");
}
return "search";
}
#RequestMapping(value="/result.xls", method=RequestMethod.GET)
public String resultXLS(Model model) throws NullPointerException {
if(listForm == null || listForm.isEmpty() == true) {
throw new NullPointerException("Can not create Excel file because no data to create from");
} else {
model.addAttribute("findAttributesXls", listForm);
}
return "xlspage";
} //End of the resultXLS(..) method
} //End of the class
Now let's play with tabs in browser:
My problem is that when I save rendered data in browser tab one as Excel file once and open new tab in browser tab two, find and render some different data in tab two again and after come back to tab one in my browser and again trying to save same data from table one as Excel file I get data from table two(latest I render), but I want to get my old data from tab one
I learn before Servlets and really interested of session synchronization. It my works in my case In servlets it just can be reached by: HttpSession session = request.getSession();
How can I do this in Spring MVC??? And does it will solve my problem??
Please give me advice.
Thank you people.
With all the best.
You can access the session in your controller method by adding a parameter HttpSession session
#RequestMapping(value="/result.xls", method=RequestMethod.GET)
public String resultXLS(HttpSession session, Model model) throws NullPointerException {
Object myObj = session.getAttribute("myAttr");
....
}
Another alternative is to have a parameter of type HttpServletRequest in the controller method
#RequestMapping(value="/result.xls", method=RequestMethod.GET)
public String resultXLS(HttpServletRequest request, Model model) throws NullPointerException {
Object myObj = request.getSession(false).getAttribute("myAttr");
....
}
But, Synchronizing the session will not solve your problem. Session synchronization is best suited for the situations where a lot of parallel requests keep coming and modifying the shared data which is stored in session. This is not the case you are talking about.
What you want is something like tab based states which is something you would not get any ready made solution, neither it's a good practice to go for. It will make your session very heavier and your web-application will not scale.
If I'm getting empty session I need to setup some values to play the action class. So, here is the method
public SearchFilters getFilters() {
return (SearchFilters) getSession().get("Filters");
}
I would like to check the session, if it's null, then I need to set the some values over here.
public SearchFilters getFilters() {
if(getSession().get("Filters").equals(null)){
---- //How to set the values and return ?
}
return (SearchFilters) getSession().get("Filters");
}
Use the code:
public SearchFilters getFilters() {
if(getSession().get("Filters") == null){
//How to set the values
getSession().put("Filters", new Filters());
}
// and return.
return (SearchFilters) getSession().get("Filters");
}
assumed you have injected the session into the action via implementing SessionAware.
The value is a free hand object which contains no value, but you could create a constructor to it and pass the value directly.
getSession() will return a new session if an existing session is not found. So you don't need to worry about this one ever returning null. Take note though, there's no get() method under HttpSession, it's getAttribute().
So you can do this:
public SearchFilters getFilters() {
if(getSession().getAttribute("Filters") == null) {
getSession().setAttribute("Filters", new SearchFilters());
}
return (SearchFilters) getSession().getAttribute("Filters");
}
Under what exact circumstances do #SessionAttributes get cleared? I've discovered some confusing behaviour when trying to use two models in a page.
When I do a GET followed by a POST using this controller...
#Controller
#RequestMapping("/myPage*")
#SessionAttributes(value = {"object1", "object2"})
public class MyController {
#RequestMapping(method = RequestMethod.GET)
public String get(Model model) {
model.addAttribute("object1", new Object1());
model.addAttribute("object2", new Object2());
return "myPage";
}
#RequestMapping(method = RequestMethod.POST)
public String post(#ModelAttribute(value = "object1") Object1 object1) {
//do something with object1
return "myPage";
}
}
...object2 gets cleared from the Model. It no longer exists as a #SessionAttribute and cannot be accessed on my view page.
However if the signature of the second method is modified to this...
public String post(#ModelAttribute(value = "object1") Object1 object1,
#ModelAttribute(value = "object2") Object2 object2) {
...then object2 does not get cleared from the model and is available on my view page.
The javadoc for #SessionAttributes says:
... attributes will be removed once
the handler indicates completion of
its conversational session.
But I don't see how I have indicated completion of the conversational session in the first example but not in the second example.
Can anyone explain this behaviour or is it a bug?
You indicate completion of the conversation by calling
SessionStatus.setComplete
public void post(...., SessionStatus status) {
status.setComplete();
}
That said, I don't see why you should be loosing one model attribute and not the other.
Have you tried doing something like:
#ModelAttribute("object1")
public Object object1() { return new Object(); }
#ModelAttribute("object2")
public Object object2() { return new Object(); }
And see how that compares to putting the attributes in the model by hand.
You can remove single session level ModelAttribute like this:
Given ModelMap model, HttpSession session and you do:
if (categoryId != null)
model.addAttribute("categoryId", categoryId);
else {
model.remove("categoryId");
session.removeAttribute("categoryId");
}