Removing default model attributes in the URL in Spring MVC while redirecting

Removing default model attributes in the URL in Spring MVC while redirecting

Recently I have come across a problem regarding page redirection. The application I was working on is using Spring security and also Spring MVC.

The problem was that when the user tries to access the /login.html the login page will be displayed if he is not logged in, but if he is already logged in then there was a problem with the URL being shown in the browser i.e. login.html (though his relevant page content is shown) as internally there was a jsp forward.

Jsp forward will not change the URL in the browser address bar. Only redirect will change the address as the browser will be asked to send another request to the server.

<div>
    <auth:authorize access="hasRole('ROLE_ADMIN')">
        <jsp:forward page="/admin/home.html"/>
    </auth:authorize>

    <auth:authorize access="hasRole('ROLE_USER')">
        <jsp:forward page="/user.html"/>
    </auth:authorize>
</div>

The above configuration from a JSP was used to forward the user to his relevant page. Since it was a jsp forward, the URL in the browser address bar was not changing.

To solve this problem I had to use Spring’s RedirectView class. So, this article shows the usage of that class and how we can avoid the default model attributes not being shown in the URL.

When we use the RedirectView class and set and return the view, it actually works fine and redirects to the specified view with the change in the URL of the browser.

But there is a caveat. It actually exposes the default model attributes in the URL like the following

http://localhost:8081/app/admin.html?total=48&loggedInUserRoles=207.

The data after ? is exposed in the URL as it the default model attribute in my application.

So, to strip off the extra data from the URL we have to set the exposeModelAttributes to false as in the following code.

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public View login(HttpServletRequest request) throws IOException {

        //Get the Spring Security authentication object
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        RedirectView view;
        String url = null;

        //Check if the user is already logged in (except the anonymous user)
        if (!(authentication instanceof AnonymousAuthenticationToken)) {
            //User is logged in
            List userRoles = AuthenticationUtils.getUserRoles();
            if (userRoles.contains("ROLE_ADMIN")) {
                url = "./admin/home.html";
            } else if (userRoles.contains("ROLE_USER")) {
                url = "./user.html";
            }
        }

        //If the user is not logged in, the url variable will be null
        //If the user is not logged in, send him to the application root, where login page will be shown
        if (url == null) {
            url = "/" + request.getContextPath();
        }

        view = new RedirectView(url);
        view.setExposeModelAttributes(false);
        return view;
    }

 

The line view.setExposeModelAttributes(false) removes the default model attributes from the URL.

Hope this article has helped you and wish you a happy coding.

Reference:

http://stackoverflow.com/questions/31187747
http://stackoverflow.com/questions/13247239

Rajasekhar
Java Developer
Helical IT Solutions

Java Servlets and Jsps Exception Logger

Dealing with logging of exceptions in a Servlets/Jsp environment.

Ideally an exception should not be caught unless it can be handled gracefully. One must use checked exceptions for all errors the application can recover from, and unchecked exceptions for the errors the application cannot recover from. But, due to lack of proper understanding(and/or due to the confusion) of Java’s Checked Exception mechanism, we find most of the code with too many catch blocks or too many throws clauses besides method declaration there by leading to exception declaration aggregation.

My intention here is not to start another war pro or against checked exceptions. FYI, C# doesn’t have the concept of checked exceptions. Personally I prefer having very few checked exceptions which can be handled or recovered from(with no empty catch blocks or re-throws by wrapping or just logging) and the rest all as unchecked exceptions.

So, while throwing unchecked exceptions, it is useful to have a centralized block of code which logs what is happening for debugging purposes. Of course, you should be pragmatic and apply it according to your condition.

Whenever there is a request for a particular Servlet, before a particular Servlet is handed over the request, we can configure a Servlet Filter to intercept and log the requests and exceptions, this way you can avoid unnecessary catch blocks through out the code base.

In this blog I will show you a code sample of how we can do it. The code also deals with preventing memory leaks(during the undeployment of the web application) regarding Jdbc drivers which are registered and shutting down the loggers.

If you are not very clear about the best practices of exception handling, here are some very good posts, which you might find useful.

Java Checked and Unchecked Exceptions

Exception Management Best Practices

Which is better? Checked or Unchecked exceptions

With a quick StackOverflow or Google search you will find many other good articles. The mentioned posts give a good understanding of the best practices. I am not showing the Servlet API specific code(Filter and Listener) as the following global methods can be called from anywhere in the code. So, you can use the following class according to your needs.

 


/**
	Utility class with methods to call from javax.servlet.Filter and javax.servlet.ServletContextListener
	for unchecked exception logging and memory clean up.
	@author Rajasekhar
*/

public class WebAppUtils {
	
    //slf4j-log4j dependency
    private static final Logger logger = LoggerFactory.getLogger(WebAppUtils.class);

    //Called from the ExceptionLogger Filter's doFilter() method, which is mapped to /*
    public static void log(ServletRequest request, ServletResponse response, FilterChain chain) {
        try {
            chain.doFilter(request, response);
        } catch (Throwable exception) {
            //Something went wrong. log the whole stacktrace for debugging. 
            //Also get the root cause
            String rootCauseMessage = ExceptionUtils.getRootCauseMessage(exception);
            //Apache-commons-lang dependency
            request.setAttribute("rootCauseMessage", rootCauseMessage);
            if (exception instanceof RuntimeException) {
                logger.error("A Runtime Exception has occurred. The cause is " + rootCauseMessage, exception);
            	//Signal Tomcat or any other server to serve the user a user friendly error page
                throw new RuntimeException();
            }
            if (exception instanceof Exception) {
                logger.error("An Exception has occurred. The cause is " + rootCauseMessage, exception);
            } else {
                //Ideally errors should not be cought, but for debugging purposes at filter level it is useful
                logger.error("An Error has occurred. The cause is " + rootCauseMessage, exception);
            }
            //Signal Tomcat or any other server to serve the user a user friendly error page
            throw new RuntimeException();
        }
    }

    //Called when context is being destoyed from a listener. 
    //I am not showing the listener configuration in web.xml
    public static void cleanUp() {
    	    //De-Register the Jdbc drivers to prevent memory leaks. 
        //Quite useful (during the hot deploys) at the time of development and testing.
        deRegisterJdbcDrivers();

        if (logger.isInfoEnabled()) {
            logger.info("Jdbc drivers have been " +
                    "de-registered to prevent memory leak. Shutting down loggers.");
        }
        LogManager.shutdown();
    }

    private static void deRegisterJdbcDrivers() {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            try {
                DriverManager.deregisterDriver(driver);
                logger.info(String.format("De-registering jdbc driver: %s", driver));
            } catch (SQLException e) {
                logger.info(String.format("Error de-registering driver %s", driver), e);
            }
        }
    }
}

The corresponding portion of web.xml:



	<filter>
	    <filter-name>logger</filter-name>
	    <filter-class>com.helical.ExceptionLogger</filter-class>
	</filter>

	<filter-mapping>
	    <filter-name>logger</filter-name>
	    <url-pattern>/*</url-pattern>
	</filter-mapping>

	//Configure Tomcat to serve the user with a user friendly error page
	<error-page>
	    <exception-type>java.lang.Throwable</exception-type>
	    <location>/WEB-INF/error.jsp</location>
	</error-page>

The error.jsp:



<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

    <div>
        Oops! Something went wrong. The cause is 
        <c:if test="${!empty requestScope.rootCauseMessage}">
            <c:out value="${requestScope.rootCauseMessage}"/>
        </c:if>
    </div>

 

Hope this helps and let me about know your opinions on this article.

B. Rajasekhar

Helical IT Solutions