ti-enxame.com

Spring MVC: atributo de solicitação de ligação para o parâmetro do método do controlador

No Spring MVC, é fácil associar request parameter aos parâmetros do método que tratam do pedido. Eu apenas uso @RequestParameter("name"). Mas posso fazer o mesmo com request attribute? Atualmente, quando quero acessar request attribute, tenho que fazer o seguinte:

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

Mas eu realmente gostaria de usar algo assim:

@RequestAttribute("attr_name") MyClass obj

Infelizmente, isso não funciona dessa maneira. Posso de alguma forma estender a funcionalidade do Spring e adicionar meus próprios "fichários"?

EDIT(o que eu estou tentando alcançar): Eu armazeno o usuário atualmente logado dentro do atributo request. Então, sempre que eu quiser acessar o usuário atualmente logado (que é praticamente dentro de cada método), eu tenho que escrever esta linha extra user = (User) request.getAttribute("user");. Eu gostaria de torná-lo o mais curto possível, de preferência, injetá-lo como um parâmetro de método. Ou se você souber de outra maneira como passar algo através de interceptadores e controladores, eu ficaria feliz em ouvir isso.

21
tobik

Bem, eu finalmente entendi um pouco como os modelos funcionam e o que é @ModelAttribute para. Aqui está a minha solução. 

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

O método getUser() marcado com a anotação @ModelAttribute preencherá automaticamente todos os parâmetros User user marcados com @ModelAttribute. Então, quando o método HandleSomeUrl é chamado, a chamada é parecida com MyController.HandleSomeUrl(MyController.getUser(request)). Pelo menos é assim que imagino isso. Coisa legal é que o usuário também é acessível a partir da visualização JSP sem qualquer esforço adicional. 

Isso resolve exatamente o meu problema, mas tenho outras dúvidas. Existe um lugar comum onde eu posso colocar esses métodos @ModelAttribute então eles eram comuns para todos os meus controllers? Posso de alguma forma adicionar o atributo de modelo de dentro do método preHandle() de um Interceptor?

33
tobik

Use (a partir da primavera 4.3) @RequestAttribute :

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

ou se o nome do atributo do pedido não corresponder ao nome do parâmetro do método:

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

Eu acho que o que você está procurando é: 

@ModelAttribute("attr_name") MyClass obj

Você pode usar isso nos parâmetros de um método no seu controlador. 

Aqui está um link para a pergunta com detalhes sobre isso O que é o @ModelAttribute no Spring MVC?

Essa questão liga-se à Documentação Spring com alguns exemplos de uso também. Você pode ver que aqui

Atualizar

Não tenho certeza de como você está configurando suas páginas, mas você pode adicionar o usuário como um Atributo de Modelo de duas formas diferentes. Eu configurei um exemplo simples abaixo aqui.

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

Então, quando o usuário envia uma solicitação, o Spring ligará o atributo do usuário ao objeto User nos parâmetros do método. 

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

Não é o mais elegante, mas funciona pelo menos ...

@Controller
public class YourController {

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

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

}

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

2
Peter Szanto

A partir da primavera 3.2, pode ser ainda melhor usando Springs ControllerAdvice annotation. Isso permitiria que você tivesse um conselho que adiciona os @ModelAttributes em uma classe separada, que é então aplicada a todos os seus controladores.

Para completar, também é possível fazer o @RequestAttribute ("nome-do-atributo") como está. (Abaixo modificado de este artigo para atender às nossas demandas)

Primeiro, temos que definir a anotação:

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

Então precisamos de um [WebArgumentResolver] para lidar com o que precisa ser feito quando o atributo está sendo vinculado

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

Agora tudo o que precisamos é adicionar este customresolver à configuração para resolvê-lo:

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

E acabamos!

1
Koos Gadellaa

Sim, você pode adicionar seus próprios 'binders' ao atributo request - veja spring-mvc-3-showcase , ou use a solução @Peter Szanto.

Como alternativa, ligue-o como um ModelAttribute, conforme recomendado em outras respostas.

Como é o usuário logado que você quer passar para o seu controlador, você pode querer considerar o Spring Security . Então você pode apenas ter o Princípio injetado em seu método:

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

No Spring WebMVC 4.x, ele prefere implementar o 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);
}

}

Então registre-o em RequestMappingHandlerAdapter

0
davidwang