1

The question is simple: Once a user is authenticated in my Spring MVC application, I'd like to show in the navigation menu the count of all unread messages, in every page.

For a specific page, I can do this inside its controller:

@RequestMapping(value = "/some/url", method = RequestMethod.GET)
public String somePage(Model model, Principal principal) {

    // Count messages and add to the view
    int countMessages = userService.countAllUnreadMessages(principal.getName());
    model.addAttribute("countMessages", countMessages);
    // ...

    return "some/view";
}

The question is how to avoid doing the same call in all methods of every controller? Is there a way to pass a data parameter to the view for all the pages? (Maybe all pages, excluding the /login form, of course)

Dani Sancas
  • 1,365
  • 11
  • 27

4 Answers4

2

You could use a controller advice, defining a @ModelAttribute annotated method that would add the message count to the model.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
2

You can use Interceptors to implement the same. Write the piece of code you want to call again and again in Interceptor and for all those you want to exclude calling of method map it in exclude mapping path.

<mvc:interceptor>
    <mvc:mapping path="/**"/>
    <mvc:exclude-mapping path="/login/**"/>
    <bean class="com.test.yourInterceptorClass" />
</mvc:interceptor>
Arpit Aggarwal
  • 27,626
  • 16
  • 90
  • 108
  • Your answer worked for me. I'm accepting your answer, but I'd like you to improve it including my `userService` call and adding the result to the `request` in the *YourInterceptorClass* `preHandle()` method. Not for me, but for the future visitors, if you please me ;) – Dani Sancas Apr 27 '15 at 21:32
1

You can store the attribute in session after the user has logged in, and access the session variable from the pages. You can annotate your controller class with @SessionAttributes("countMessages") which will store your value in the session as well once that you put it in the model. Pages would access it the same way as you access your model variable, via ${countMessages}.

You can also work with the session explicitely e.g.

@RequestMapping(value = "/some/url", method = RequestMethod.GET)
public String somePage(Model model, Principal principal, HttpSession session) {

    // Count messages and add to the view
    int countMessages = userService.countAllUnreadMessages(principal.getName());
    model.addAttribute("countMessages", countMessages);
    session.addAttribute("countMessages", countMessages);
    // ...

    return "some/view";
}

but note that the session answers this part of your question Is there a way to pass a data parameter to the view for all the pages? Now whenever there's a change in the number of messages read you would have to update the session value as well, wich you can do by simply calling this controller method again.

Other answers provide a way to have the automatically updated count without the explicit call, either by intercepting request or calling the @ModelAttribute annotated method on a @ControllerAdvice perhaps that is better suited for your problem

Master Slave
  • 27,771
  • 4
  • 57
  • 55
  • A couple of related questions. 1st: How can I add that initial count to the session? Is the session available in CustomUserDetailsService class? 2nd: When the user reads a message, the new count would be re-calculated in the controller. Which way should I use to update the `countMessages` session attribute? – Dani Sancas Apr 27 '15 at 13:23
  • I've edited an answer a bit, but my answer only gives you means to have the value available in any page. If you need to automatically maintain the count, without the explicit call to controller than other answers seem a better fit – Master Slave Apr 27 '15 at 13:44
0

There multiple ways to achieve what you want. I'll suggest you to go with a custom filter, that will be used as a "decorator", and will add your desired data to request.

public class MessagesFilter implements Filter{

@Override
public void destroy() {
    // Do nothing
}

@Override
public void doFilter(ServletRequest req, ServletResponse res,
        FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

       int countMessages = userService.countAllUnreadMessages(authentication.getName());
        req.addAttribute("countMessages", countMessages);

        chain.doFilter(req, res);

}

@Override
public void init(FilterConfig arg0) throws ServletException {
    // Do nothing
}

}

or

public class MessagesFilter extends GenericFilterBean{
@Override
public void doFilter(ServletRequest request, ServletResponse response,
        FilterChain chain) throws IOException, ServletException {
    //Implement this Function to have your filter working
}

see this answer for proper examples

Community
  • 1
  • 1
Eugen Halca
  • 1,775
  • 2
  • 13
  • 26