tobik
tobik

Reputation: 7198

Spring MVC: bind request attribute to controller method parameter

In Spring MVC, it is easy to bind request parameter to method paramaters handling the request. I just use @RequestParameter("name"). But can I do the same with request attribute? Currently, when I want to access request attribute, I have to do following:

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

But I really would like to use something like this instead:

@RequestAttribute("attr_name") MyClass obj

Unfortunately, it doesn't work this way. Can I somehow extend Spring functionality and add my own "binders"?

EDIT (what I'm trying to achieve): I store currently logged user inside request attribute. So whenever I want to access currently logged user (which is pretty much inside every method), I have to write this extra line user = (User) request.getAttribute("user");. I would like to make it as short as possible, preferably inject it as a method parameter. Or if you know another way how to pass something across interceptors and controllers, I would be happy to hear it.

Upvotes: 23

Views: 88772

Answers (7)

Brice Roncace
Brice Roncace

Reputation: 10650

Use (as of Spring 4.3) @RequestAttribute:

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

or if the request attribute name does not match the method parameter name:

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

Upvotes: 15

davidwang
davidwang

Reputation: 1

In Spring WebMVC 4.x, it prefer implements 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);
}

}

Then register it in RequestMappingHandlerAdapter

Upvotes: 0

whistling_marmot
whistling_marmot

Reputation: 3893

Yes, you can add your own 'binders' to the request attribute - see spring-mvc-3-showcase, or use @Peter Szanto's solution.

Alternatively, bind it as a ModelAttribute, as recommended in other answers.

As it's the logged-in user that you want to pass into your controller, you may want to consider Spring Security. Then you can just have the Principle injected into your method:

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

Upvotes: 0

Peter Szanto
Peter Szanto

Reputation: 7722

Not the most elegant, but works at least...

@Controller
public class YourController {

    @RequestMapping("/xyz")
    public ModelAndView handle(
        @Value("#{request.getAttribute('key')}") SomeClass obj) {

        ...
        return new ModelAndView(...);
    }

}

Source : http://blog.crisp.se/tag/requestattribute

Upvotes: 2

Koos Gadellaa
Koos Gadellaa

Reputation: 1254

From spring 3.2 it can be done even nicer by using Springs ControllerAdvice annotation. This then would allow you to have an advice which adds the @ModelAttributes in a separate class, which is then applied to all your controllers.

For completeness, it is also possible to actually make the @RequestAttribute("attr-name") as is. (below modified from this article to suit our demands)

First, we have to define the annotation:

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

Then we need a [WebArgumentResolver] to handle what needs to be done when the attribute is being bound

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;
    }
}

Now all we need is to add this customresolver to the config to resolve it:

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

And we're done!

Upvotes: 1

tobik
tobik

Reputation: 7198

Well, I finally understood a little bit how models work and what is @ModelAttribute for. Here is my solution.

@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
    }
}

The getUser() method marked with @ModelAttribute annotation will automatically populate all User user parameters marked with @ModelAttribute. So when the HandleSomeUrl method is called, the call looks something like MyController.HandleSomeUrl(MyController.getUser(request)). At least this is how I imagine it. Cool thing is that user is also accessible from the JSP view without any further effort.

This solves exactly my problem however I do have further questions. Is there a common place where I can put those @ModelAttribute methods so they were common for all my controllers? Can I somehow add model attribute from the inside of the preHandle() method of an Interceptor?

Upvotes: 37

ssn771
ssn771

Reputation: 1270

I think what you are looking for is:

@ModelAttribute("attr_name") MyClass obj

You can use that in the parameters for a method in your controller.

Here is a link a to question with details on it What is @ModelAttribute in Spring MVC?

That question links to the Spring Documentation with some examples of using it too. You can see that here

Update

I'm not sure how you are setting up your pages, but you can add the user as a Model Attribute a couple different ways. I setup a simple example below 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
}

Then when the user submits a request, Spring will bind the user attribute to the User object in the method parameters.

@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);
}

Upvotes: 3

Related Questions