Changing Spring Model Parameters with Handler Interceptor – 用处理程序拦截器改变Spring模型参数

最后修改: 2016年 8月 24日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

In this tutorial we are going to focus on the Spring MVC HandlerInterceptor. More specifically, we will change Spring MVC’s model parameters before and after handling a request.

在本教程中,我们将专注于Spring MVC的HandlerInterceptor。更具体地说,我们将在处理一个请求之前和之后改变Spring MVC的模型参数。

If you want to read about HandlerInterceptor’s basics, check out this article.

如果你想阅读关于HandlerInterceptor的基础知识,请查看这篇文章

2. Maven Dependencies

2.Maven的依赖性

In order to use Interceptors, you need to include the following section in a dependencies section of your pom.xml file:

为了使用Interceptors,你需要在pom.xml文件的dependencies部分包含以下部分。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-web</artifactId>
    <version>5.3.13</version>
</dependency>

The latest version can be found here.

最新版本可以在这里找到。

This dependency only covers Spring Web so don’t forget to add spring-core and spring-context for a full web application, and a logging library of your choice.

这个依赖关系只包括Spring Web,所以别忘了为完整的Web应用添加spring-corespring-context,以及一个你选择的日志库。

3. Custom Implementation

3.自定义实施

One of the use cases of HandlerInterceptor is adding common/user specific parameters to a model, which will be available on each generated view.

HandlerInterceptor的一个用例是向模型添加普通/用户特定的参数,这些参数将在每个生成的视图上可用。

In our example, we will use custom interceptor implementation to add logged user’s username to model parameters. In more complex systems we may add more specific information like: user avatar path, user location, etc.

在我们的例子中,我们将使用自定义拦截器实现,将登录用户的用户名添加到模型参数中。在更复杂的系统中,我们可以添加更具体的信息,比如:用户头像路径,用户位置等。

Let’s start with defining our new Interceptor class:

让我们从定义我们的新拦截器类开始。

public class UserInterceptor extends HandlerInterceptorAdapter {

    private static Logger log = LoggerFactory.getLogger(UserInterceptor.class);

    ...
}

We extend HandlerInterceptorAdapter, as we only want to implement preHandle() and postHandle() methods.

我们扩展HandlerInterceptorAdapter,因为我们只想实现preHandle()postHandle()方法。

As we mentioned before, we want to add logged user’s name to a model. First of all, we need to check if a user is logged in. We may obtain this information by checking SecurityContextHolder:

正如我们之前提到的,我们想把登录的用户的名字添加到一个模型中。首先,我们需要检查一个用户是否已经登录。我们可以通过检查SecurityContextHolder获得这一信息。

public static boolean isUserLogged() {
    try {
        return !SecurityContextHolder.getContext().getAuthentication()
          .getName().equals("anonymousUser");
    } catch (Exception e) {
        return false;
    }
}

When an HttpSession is established, but nobody is logged in, a username in Spring Security context equals to anonymousUser. Next, we proceed with implementation of preHandle():

当一个HttpSession被建立,但没有人登录,Spring Security上下文中的一个用户名等于anonymousUser。接下来,我们继续实现preHandle():

3.1. Method preHandle()

3.1.方法preHandle()

Before handling a request, we cannot access model parameters. In order to add username, we need to use HttpSession to set parameters:

在处理一个请求之前,我们不能访问模型参数。为了添加用户名,我们需要使用HttpSession来设置参数。

@Override
public boolean preHandle(HttpServletRequest request,
  HttpServletResponse response, Object object) throws Exception {
    if (isUserLogged()) {
        addToModelUserDetails(request.getSession());
    }
    return true;
}

This is crucial if we are using some of this information before handling a request. As we see, we are checking if a user is logged in and then add parameters to our request by obtaining its session:

如果我们在处理一个请求之前使用其中的一些信息,这就很关键。正如我们所看到的,我们正在检查一个用户是否已经登录,然后通过获取其会话向我们的请求添加参数。

private void addToModelUserDetails(HttpSession session) {
    log.info("=============== addToModelUserDetails =========================");
    
    String loggedUsername 
      = SecurityContextHolder.getContext().getAuthentication().getName();
    session.setAttribute("username", loggedUsername);
    
    log.info("user(" + loggedUsername + ") session : " + session);
    log.info("=============== addToModelUserDetails =========================");
}

We used SecurityContextHolder to obtain loggedUsername. You may override Spring Security UserDetails implementation to obtain email instead of a standard username.

我们使用SecurityContextHolder来获取loggedUsername。你可以覆盖Spring Security的UserDetails实现来获取电子邮件而不是标准的用户名。

3.2. Method postHandle()

3.2.方法 postHandle()

After handling a request, our model parameters are available, so we may access them to change values or add new ones. In order to do that, we use the overridden postHandle() method:

在处理完一个请求后,我们的模型参数是可用的,所以我们可以访问它们以改变数值或添加新的参数。为了做到这一点,我们使用重载的postHandle() 方法。

@Override
public void postHandle(
  HttpServletRequest req, 
  HttpServletResponse res,
  Object o, 
  ModelAndView model) throws Exception {
    
    if (model != null && !isRedirectView(model)) {
        if (isUserLogged()) {
        addToModelUserDetails(model);
    }
    }
}

Let’s take a look at the implementation details.

让我们来看看实施细节。

First of all, it’s better to check if the model is not null. It will prevent us from encountering a NullPointerException.

首先,最好检查模型是不是null。这将防止我们遇到NullPointerException

Moreover, we may check if a View is not an instance of RedirectView.

此外,我们可以检查一个View是否不是RedirectView的实例。

There is no need to add/change parameters after the request is handled and then redirected, as immediately, the new controller will perform handling again. To check if the view is redirected, we are introducing the following method:

在请求被处理并重定向后,不需要添加/改变参数,因为立即,新的控制器将再次执行处理。为了检查视图是否被重定向,我们将引入以下方法。

public static boolean isRedirectView(ModelAndView mv) {
    String viewName = mv.getViewName();
    if (viewName.startsWith("redirect:/")) {
        return true;
    }
    View view = mv.getView();
    return (view != null && view instanceof SmartView
      && ((SmartView) view).isRedirectView());
}

Finally, we are checking again if a user is logged, and if yes, we are adding parameters to Spring model:

最后,我们再次检查是否有用户登录,如果有,我们要向Spring模型添加参数。

private void addToModelUserDetails(ModelAndView model) {
    log.info("=============== addToModelUserDetails =========================");
    
    String loggedUsername = SecurityContextHolder.getContext()
      .getAuthentication().getName();
    model.addObject("loggedUsername", loggedUsername);
    
    log.trace("session : " + model.getModel());
    log.info("=============== addToModelUserDetails =========================");
}

Please note that logging is very important, as this logic works “behind the scenes” of our application. It is easy to forget that we are changing some model parameters on each View without logging it properly.

请注意,日志记录是非常重要的,因为这个逻辑在我们的应用程序的 “幕后 “工作。我们很容易忘记我们正在改变每个View上的一些模型参数,而没有正确地记录它。

4. Configuration

4.配置

To add our newly created Interceptor into Spring configuration, we need to override addInterceptors() method inside WebConfig class that implements WebMvcConfigurer:

为了将我们新创建的拦截器添加到Spring配置中,我们需要在实现WebMvcConfigurer的WebConfig类中覆盖addInterceptors()方法。

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(new UserInterceptor());
}

We may achieve the same configuration by editing our XML Spring configuration file:

我们可以通过编辑我们的XML Spring配置文件实现同样的配置。

<mvc:interceptors>
    <bean id="userInterceptor" class="com.baeldung.web.interceptor.UserInterceptor"/>
</mvc:interceptors>

From this moment, we may access all user-related parameters on all generated views.

从这一刻起,我们可以在所有生成的视图上访问所有与用户有关的参数。

Please notice, if multiple Spring Interceptors are configured, the preHandle() method is executed in the order of configuration whereas postHandle() and afterCompletion() methods are invoked in the reverse order.

请注意,如果配置了多个Spring InterceptorspreHandle()方法将按照配置顺序执行,而postHandle()afterCompletion()方法将按照相反顺序调用。

5. Conclusion

5.结论

This tutorial presents intercepting web requests using Spring MVC’s HandlerInterceptor in order to provide user information.

本教程介绍了使用Spring MVC的HandlerInterceptor拦截Web请求,以提供用户信息。

In this particular example, we focused on adding logged user’s details in our web application to model parameters. You may extend this HandlerInterceptor implementation by adding more detailed information.

在这个特殊的例子中,我们专注于在我们的Web应用程序中添加登录用户的详细信息到模型参数。你可以通过添加更详细的信息来扩展这个HandlerInterceptor实现。

All examples and configurations are available here on GitHub.

所有的例子和配置都可以在GitHub上找到。

5.1. Articles in the Series

5.1.丛书中的文章

All articles of the series:

该系列的所有文章。