Spring MVC:将请求属性绑定到控制器方法参数

Posted

技术标签:

【中文标题】Spring MVC:将请求属性绑定到控制器方法参数【英文标题】:Spring MVC: bind request attribute to controller method parameter 【发布时间】: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");。我想让它尽可能短,最好将它作为方法参数注入。或者,如果您知道如何通过拦截器和控制器传递某些东西的另一种方式,我会很高兴听到它。

【问题讨论】:

【参考方案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中注册

【讨论】:

以上是关于Spring MVC:将请求属性绑定到控制器方法参数的主要内容,如果未能解决你的问题,请参考以下文章

Spring MVC Data Binding

spring mvc框架常用注解及用途

Spring MVC 基础篇4

Spring MVC 基础篇 2

.NET Core MVC - 带有前缀绑定的 AJAX POST 请求

Spring MVC常用注解(下)