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()
方法将自动填充所有标有@ModelAttribute
的User 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 对象添加到模型中。然后你就可以访问它了。有几种方法可以做到这一点。我会更新我的答案。
我对@987654329@ 不太熟悉,但这也可能对您有用。这是关于它的文档的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:将请求属性绑定到控制器方法参数的主要内容,如果未能解决你的问题,请参考以下文章