【问题标题】:Spring MVC: bind request attribute to controller method parameterSpring MVC:将请求属性绑定到控制器方法参数
【发布时间】:2013-02-08 12:52:09
【问题描述】:

在 Spring MVC 中,很容易将请求参数绑定到处理请求的方法参数。我只使用@RequestParameter("name")。但是我可以对请求 attribute 做同样的事情吗?目前,当我想访问请求属性时,我必须执行以下操作:

MyClass obj = (MyClass) request.getAttribute("attr_name");

但我真的很想改用这样的东西:

@RequestAttribute("attr_name") MyClass obj

不幸的是,它不能这样工作。我可以以某种方式扩展 Spring 功能并添加我自己的“绑定器”吗?

编辑 (我想要实现的):我将当前登录的用户存储在请求属性中。因此,每当我想访问当前登录的用户(几乎在每个方法中)时,我都必须编写这个额外的行user = (User) request.getAttribute("user");。我想让它尽可能短,最好将它作为方法参数注入。或者,如果您知道如何通过拦截器和控制器传递某些东西的另一种方式,我会很高兴听到它。

【问题讨论】:

    标签: java spring spring-mvc


    【解决方案1】:

    嗯,我终于明白了一点模型的工作原理以及@ModelAttribute 的用途。这是我的解决方案。

    @Controller 
    class MyController
    {
        @ModelAttribute("user")
        public User getUser(HttpServletRequest request) 
        {
            return (User) request.getAttribute("user");
        }
    
        @RequestMapping(value = "someurl", method = RequestMethod.GET)
        public String HandleSomeUrl(@ModelAttribute("user") User user)  
        {
            // ... do some stuff
        }
    }
    

    标有@ModelAttribute 注释的getUser() 方法将自动填充所有标有@ModelAttributeUser user 参数。因此,当调用 HandleSomeUrl 方法时,调用看起来像 MyController.HandleSomeUrl(MyController.getUser(request))。至少我是这样想象的。很酷的是,用户也可以从 JSP 视图中访问,而无需任何进一步的努力。

    这完全解决了我的问题,但是我还有其他问题。是否有一个共同的地方可以放置这些 @ModelAttribute 方法,以便它们对我的所有控制器都是通用的?我可以以某种方式从拦截器的preHandle() 方法内部添加模型属性吗?

    【讨论】:

    • 这太完美了!谢谢。
    • 如果您的参数名称与模型对象上的属性匹配,则不需要额外的 getUser 方法,spring 会自动设置这些属性
    • 这样,您方法中的 User 对象不仅会从视图模型中检索,而且其属性也将被传入的请求或表单参数绑定(并覆盖)。例如,如果 User 对象有一个 id 属性(许多用户对象都会有)并且请求 url 或表单也有一个不相关的 id 参数,它将覆盖 User 对象的 id。这曾经在我的代码中造成了一个令人讨厌的错误。在这种特殊情况下,您不需要@ModelAttribute 的表单绑定属性。另一种方法是使用User u = (User) model.get("user");
    【解决方案2】:

    使用(从 Spring 4.3 开始)@RequestAttribute:

    @RequestMapping(value = "someurl", method = RequestMethod.GET)
    public String handleSomeUrl(@RequestAttribute User user) {
        // ... do some stuff
    }
    

    或者如果请求属性名与方法参数名不匹配:

    @RequestMapping(value = "someurl", method = RequestMethod.GET)
    public String handleSomeUrl(@RequestAttribute(name="userAttributeName") User user) {
        // ... do some stuff
    }
    

    【讨论】:

      【解决方案3】:

      我认为您正在寻找的是:

      @ModelAttribute("attr_name") MyClass obj
      

      您可以在控制器中的方法的参数中使用它。

      这是一个问题的链接What is @ModelAttribute in Spring MVC?

      该问题链接到 Spring 文档,其中也包含一些使用示例。可以看到here

      更新

      我不确定您是如何设置页面的,但您可以通过几种不同的方式将用户添加为模型属性。我在这里设置了一个简单的例子。

      @RequestMapping(value = "/account", method = RequestMethod.GET)
      public ModelAndView displayAccountPage() {
          User user = new User(); //most likely you've done some kind of login step this is just for simplicity
          return new ModelAndView("account", "user", user); //return view, model attribute name, model attribute
      }
      

      那么当用户提交请求时,Spring会将用户属性绑定到方法参数中的User对象。

      @RequestMapping(value = "/account/delivery", method = RequestMethod.POST)
      public ModelAndView updateDeliverySchedule(@ModelAttribute("user") User user) {
      
          user = accountService.updateDeliverySchedule(user); //do something with the user
      
          return new ModelAndView("account", "user", user);
      }
      

      【讨论】:

      • 谢谢,但我试图实现其他目标.. 也许我应该解释一下。我使用 request 属性来存储当前登录的用户。所以就像你打电话给User user = (User) session.getAttribute("user"); 我打电话给user = (User) request.getAttribute("user");。但是在每一个方法中都写这个是很烦人的。我想让它尽可能短,最好将它作为方法参数注入。
      • 您仍然可以使用此方法来执行此操作。您只需要将 User 对象添加到模型中。然后你就可以访问它了。有几种方法可以做到这一点。我会更新我的答案。
      • 我对@9​​87654329@ 不太熟悉,但这也可能对您有用。这是关于它的文档的link
      • 好吧,我不确定我是否理解你,但你肯定把我引向了正确的方向,我找到了我一直在寻找的东西。所以谢谢,过一会儿看看我自己的答案:)
      【解决方案4】:

      不是最优雅的,但至少可以工作......

      @Controller
      public class YourController {
      
          @RequestMapping("/xyz")
          public ModelAndView handle(
              @Value("#{request.getAttribute('key')}") SomeClass obj) {
      
              ...
              return new ModelAndView(...);
          }
      
      }
      

      来源:http://blog.crisp.se/tag/requestattribute

      【讨论】:

        【解决方案5】:

        从 spring 3.2 开始,使用 Springs ControllerAdvice 注释可以做得更好。 然后,这将允许您获得一个建议,将 @ModelAttributes 添加到一个单独的类中,然后将其应用于您的所有控制器。

        为了完整起见,也可以按原样实际制作@RequestAttribute("attr-name")。 (以下从this article修改以适应我们的需求)

        首先,我们要定义注解:

        @Retention(RetentionPolicy.RUNTIME)
        @Target(ElementType.PARAMETER)
        public @interface RequestAttribute {
            String value();
        }
        

        那么我们需要一个 [WebArgumentResolver] 来处理绑定属性时需要做的事情

        public class RequestAttributeWebArgumentResolver implements WebArgumentResolver {
        
            public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest nativeWebRequest) throws Exception {
                // Get the annotation       
                RequestAttribute requestAttributeAnnotation = methodParameter.getParameterAnnotation(RequestAttribute.class);
        
                if(requestAttributeAnnotation != null) {
                    HttpServletRequest request = (HttpServletRequest) nativeWebRequest.getNativeRequest();
        
                    return request.getAttribute(requestAttributeAnnotation.value);
                }
        
                return UNRESOLVED;
            }
        }
        

        现在我们只需要将此 customresolver 添加到配置中即可解决它:

        <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
            <property name="customArgumentResolver">
                <bean class="com.sergialmar.customresolver.web.support.CustomWebArgumentResolver"/>
            </property>
        </bean>
        

        我们完成了!

        【讨论】:

          【解决方案6】:

          是的,您可以将自己的“绑定器”添加到请求属性 - 请参阅 spring-mvc-3-showcase,或使用 @Peter Szanto 的解决方案。

          或者,按照其他答案中的建议,将其绑定为 ModelAttribute。

          由于您要传递到控制器中的是登录用户,因此您可能需要考虑Spring Security。然后您可以将Principle 注入您的方法中:

          @RequestMapping("/xyz")
          public String index(Principal principle) {
              return "Hello, " + principle.getName() + "!";
          }
          

          【讨论】:

            【解决方案7】:

            在 Spring WebMVC 4.x 中,它更喜欢实现 HandlerMethodArgumentResolver

            @Override
            public boolean supportsParameter(MethodParameter parameter) {
                return parameter.getParameterAnnotation(RequestAttribute.class) != null;
            }
            
            @Override
            public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                    NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
                return webRequest.getAttribute(parameter.getParameterAnnotation(RequestAttribute.class).value(), NativeWebRequest.SCOPE_REQUEST);
            }
            

            }

            然后在RequestMappingHandlerAdapter中注册

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2014-02-02
              • 1970-01-01
              • 1970-01-01
              • 2016-08-18
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-11-22
              相关资源
              最近更新 更多