To show the user's name on every Freemarker page, I could call model.addAttribute in every controller as below:
#RequestMapping(value = "index",method=RequestMethod.GET)
public String index(Model model) {
model.addAttribute("currentUser", App.getCurrentUser());
return "index";
}
#index.ftl
<div>${currentUser.userName}</div>
The calling would appear in everywhere of my code. It's really a nightmare. Is there any other way like AOP or Servlet Filter to set stuff into page?
You can use and interceptor for this, please check: http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html#mvc-handlermapping-interceptor
This kind of information it would be better to keep it on a session scoped bean holding the user profile, rather than reloading it for every HTTP request.
Related
I am not sure it this is possible at all. I see that in Facebook when you crate a business page you will get a link with page number, for example:
https://www.facebook.com/degendaUK/
I would like to know if it is possible to create a link like that without having an HTML or JSP page called "DegendaUK" for example.
In my code I have page
http://localhost:8080/offers/empresa?get_Business_ID=29-11-2017-03:39:22R7M5NZ8ZAL
The standard page is called "Empresa" and then I pass the ID so I can query the database.
Is there anyway that instead of my URL I would get
http://localhost:8080/offers/BUSINESS-NAME
without creating a JSP page for each business?
I am using Spring MVC.
You may use Spring #Controller, #RequestMapping and #PathVariable annotations to do this.
#Controller
public class Controller
{
#RequestMapping(value = "/offers/{id}")
public String offer(#PathVariable String id, final Model model)
{
//pass the value from the url to your jsp-view, access it via ${id} from
//there
model.add("id",id);
//render the page "empressa.jsp"
return "empressa";
}
}
Hint: You may need some and in your XML config to make those annotations work.
If your using spring-boot, this should be preconfigured already an work out of the box.
Don't forget to secure those things if they're not public things using spring-security :)
I have an Spring-mvc application and in each controller I add a form to SessionAttributes to preserve properties when save, delete or do another get request. Main problem becomes when I try to open some link in another browser tab and try to submit the first one. I tried this solution but when I do a redirect (in controller I only have 1 return for view and the other methods do a redirect) it creates a new conversation and can't find previous one.
I have another question about this triying to use spring-session, question It's here but I don't know if this will work too.
Did you look into Spring's RedirectAttributes? I haven't used it myself but it sounds like it should do what you would like. RedirectAttributes is typically used for GET/redirect/POST patterns and at least one user seems to think passing session attributes this way is bad practice, however they go on to mention there doesn't seem to be a better solution. Anyway, the example shown in the documentation:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handle(Account account, BindingResult result, RedirectAttributes redirectAttrs) {
if (result.hasErrors()) {
return "accounts/new";
}
// Save account ...
redirectAttrs.addAttribute("id", account.getId()).addFlashAttribute("message", "Account created!");
return "redirect:/accounts/{id}";
}
would add the "message" attribute to a RedirectModel, and if your controller redirects, then whatever method handles the redirect can access that data like so:
#RequestMapping(value = "/accounts", method = RequestMethod.POST)
public String handleRedirect(Model model) {
String message = (String) model.asMap().get("message");
return new ModelAndView();
}
So adding session attributes should be possible in the same way. Another reference here.
EDIT
I was looking through the Spring documentation and they also mention this annotation #SessionAttributes. From the documentation:
The type-level #SessionAttributes annotation declares session attributes used by a specific handler. This will typically list the names of model attributes or types of model attributes which should be transparently stored in the session or some conversational storage, serving as form-backing beans between subsequent requests.
Could this be what you need?
And also a link to documentation on flash attributes.
This is the solution we have come up with, nothing to do with Spring:
On each html form of your application you will have to include a hidden field. Let's name this field CSRF_TOKEN. This field should have a randomly generated value. This value is placed both in the session and the hidden field. The name of the session attribute is SESSION_CSRF_TOKEN
When the form is submitted to the server, you check whether the value in the session (SESSION_CSRF_TOKEN) equals the value sent in the HTTP request parameter CSRF_TOKEN. If not, you show some kind of error message and you stop processing. If they are equal, proceed.
If the user opens a new tab or duplicates a tab, the server will re-render the page and a new CSRF_TOKEN will be generated. So the user will only be able to submit the form from the newly opened tab , and not from the original.
This solution offers an additional bonus: It protects from CSRF attacks.
I'm doing a redirect from a Spring MVC controller by returning a String containing the URL:
return "redirect:/my/form/newpage.html?pid=".concat(myform.getId().toString());
this gives a string like this:
redirect:/my/form/newpage.html?pid=456
The trouble is, the Spring ModelFactory class appends all our session attributes to the query string and it looks horrible. I'd really like to change this redirect from a GET to a POST, but I have no idea how to do that. Can anyone help?
You can't really change the HTTP Method of redirect but
you can try this to avoid exposing variables to path (instead these explicitly added like pid):
public ModelAndView redirectToSomewhere() {
RedirectView redirectView = new RedirectView("/my/form/newpage.html?pid=".concat(myform.getId().toString());
redirectView.setExposeModelAttributes(false); // these
redirectView.setExposePathVariables(false); //two depend on the way you set your variables
return new ModelAndView(redirectView);
}
I was wondering if there is a better way to handle handling sessions than running this set of code through each one of my controller methods.
public ModelAndView addUser(#RequestParam("userid") String userId,
#RequestParam("passwd") String passwd,
#RequestParam("usrtype") String usrtype,
HttpSession session,
Model model ){
ModelAndView mav = new ModelAndView();
if ((String) session.getAttribute("userId") == null) {
model.addAttribute("msg", "Session was terminated.");
model.addAttribute("url", "/login");
mav.setViewName("redirect");
return mav;
}
...
How would one go about making this into reusable code?
There are multiple ways to optimize this:
Securing requests is something Spring Security is made for. Spring Security uses a Servlet filter to intercept (and deny) requests before they arrive in your controller. So you do not have to handle security related code in controller actions
If, for whatever reason, you can/want not use Spring Security you should have a look at Spring's MVC interceptions. In interceptors you can place code that need to be executed before and after controller actions.
If you always need to set the same Model attribute you can annotate methods with #ModelAttribute. This method will then be called for every request to populate your model, see ModelAttribute methods documentation. ControllerAdvice is similar, it is used if other classes than the controller should provide model information.
I'm creating a Spring application and in it I have a side bar which can be use to access modules of the application.
The side bar remains visible through out all views of the application. I create the view using Apache Tiles.
Since I don't want to hard code the menu items in the JSP, I want to populate required data(Link name, URL pattern) from database.
I want to do this only once so my plan is to create a session bean to put all these menu details in to it.
I create a Class called "MenuDAO" and I autowire it to the controller. Then I create a method to return that "MenuDAO" object and I mark it as a "ModelAttribute" in my controller.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("userDetails")
public UserDetail getUserDetail(){
return this.userDetails;
}
Now I am wandering where can I fill this DAO with data. I want to do it once per session(like when user logged in).
I cannot do it in the method with request mapping because once it get called Model attributes are already injected so only when the page loads next time the new data will be available.
Again I thought of doing it inside a method annotated with #PostConstruct. But then I need to do it in every Controller.
So I want to know where I can do this correctly so that I can populate this bean once per user session.
Check this question it is quite similar :
Dynamic content in all page in Spring MVC
You will have to populate your modelAndView in a postHandle method of an interceptor.
The interceptors are called for every request so it is your responsability to not call your dao for every request.
I would suggest you to store your menu in a cache instead of putting it in the session (using ehcache which is easy to set up in spring) .
I solve it as follows. I don't feel that this is the most convenient way. But it works.
I autowire session bean and mark required attribute as model attrbutes.
#Autowired
private MenuDAO menuDAO;
#ModelAttribute("menuDetails")
public List<ElabModuleWebProperties> getMenuDetails(){
return this.menuDAO.getMenuList();
}
After authenticate I redirect to url patter "home" by Spring Security. In that method I check whether the session bean has initialized and if not I do it there.
#RequestMapping(value = "home", method = RequestMethod.GET)
public String showHome(ModelMap model, Principal principal){
logger.debug("User accessing home");
if(!menuDAO.isMenuInitiationDone()){
logger.debug("menuDAO is empty, populating data for the object");
menuDAO.setMenuList(loginService.loadUserDetailsByUsername(SecurityContextHolder.getContext().getAuthentication().getName()));
menuDAO.setMenuInitiationDone(true);
}
model.put("menuDetails", menuDAO.getMenuList());
return "welcome";
}
In other controllers I can just autowire the bean and use it. No problem.