java - multiple - ¿Cómo evito que las variables Spring 3.0 MVC @ ModelAttribute aparezcan en la URL?
spring form docs (13)
Aquí está cómo hacerlo con la configuración basada en Java (Spring 3.1+ Creo, probado con 4.2):
@Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
@Override
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter();
adapter.setIgnoreDefaultModelOnRedirect(true);
return adapter;
}
// possible other overrides as well
}
Utilizando Spring MVC 3.0.0.RELEASE, tengo el siguiente Controlador:
@Controller
@RequestMapping("/addIntake.htm")
public class AddIntakeController{
private final Collection<String> users;
public AddIntakeController(){
users = new ArrayList<String>();
users.add("user1");
users.add("user2");
// ...
users.add("userN");
}
@ModelAttribute("users")
public Collection<String> getUsers(){
return this.users;
}
@RequestMapping(method=RequestMethod.GET)
public String setupForm(ModelMap model){
// Set up command object
Intake intake = new Intake();
intake.setIntakeDate(new Date());
model.addAttribute("intake", intake);
return "addIntake";
}
@RequestMapping(method=RequestMethod.POST)
public String addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){
// Validate Intake command object and persist to database
// ...
String caseNumber = assignIntakeACaseNumber();
return "redirect:intakeDetails.htm?caseNumber=" + caseNumber;
}
}
El controlador lee la información de entrada de un objeto de comando llenado de un formulario HTML, valida el objeto de comando, persiste la información en la base de datos y devuelve un número de caso.
Todo funciona muy bien, excepto cuando me redirecciona a la página intakeDetails.htm, obtengo una URL que se ve así:
http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...
¿Cómo evito que la Colección de usuarios se muestre en la URL?
Desde la primavera 3.1, RequestMappingHandlerAdapter
proporciona un indicador llamado ignoreDefaultModelOnRedirect
que puede usar para evitar el uso del contenido del modelo defautl si el controlador redirige.
En mi aplicación, no tengo ningún caso de uso para exponer los atributos del modelo en redirección, así que extendí org.springframework.web.servlet.view.UrlBasedViewResolver para reemplazar el método createView y lo usé como declarado en el contexto de la aplicación:
public class UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect extends UrlBasedViewResolver {
@Override
protected View createView(String viewName, Locale locale) throws Exception {
// If this resolver is not supposed to handle the given view,
// return null to pass on to the next resolver in the chain.
if (!canHandle(viewName, locale)) {
return null;
}
// Check for special "redirect:" prefix.
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
boolean exposeModelAttributes = false;
return new RedirectView(redirectUrl, isRedirectContextRelative(), isRedirectHttp10Compatible(), exposeModelAttributes);
}
// Check for special "forward:" prefix.
if (viewName.startsWith(FORWARD_URL_PREFIX)) {
String forwardUrl = viewName.substring(FORWARD_URL_PREFIX.length());
return new InternalResourceView(forwardUrl);
}
// Else fall back to superclass implementation: calling loadView.
return super.createView(viewName, locale);
}
}
<bean id="viewResolver" class="com.acme.spring.UrlBasedViewResolverWithouthIncludingModeAtttributesInRedirect">
</bean>
Hay una solución alternativa si ayuda a tu causa.
@ModelAttribute("users")
public Collection<String> getUsers(){
return this.users;
}
Aquí ha hecho que devuelva Collection of String. Conviértalo en una Colección de usuarios (puede ser una cadena de envoltura de clase que representa a un usuario, o una clase con una gran cantidad de datos sobre un usuario). El problema ocurre solo con cadenas. Si la Colección devuelta contiene cualquier otro objeto, esto nunca sucede. Sin embargo, esto es solo una solución, y puede ser, no requerido en absoluto. Solo mis dos centavos. Solo hazlo como ...
@ModelAttribute("users")
public Collection<User> getUsers(){
return this.users;
}
Implementé una variante de la respuesta de Sid con menos copiar y pegar involucrado:
public class RedirectsNotExposingModelUrlBasedViewResolver extends UrlBasedViewResolver {
@Override
protected View createView(String viewName, Locale locale) throws Exception {
View view = super.createView(viewName, locale);
if (view instanceof RedirectView) {
((RedirectView) view).setExposeModelAttributes(false);
}
return view;
}
}
Esto también requiere que se defina un bean resolutor de vista:
<bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/>
</bean>
La anotación del método @ModelAttribute
está destinada a ser utilizada para exponer datos de referencia a la capa de visualización. No puedo asegurarlo en su caso, pero no diría que una colección de usuarios calificó como datos de referencia. Le sugiero que pase esta información al modelo explícitamente en su @RequestMapping-métodos de controlador anotados.
Si aún desea utilizar @ModelAttribute
, aquí hay una entrada de blog que trata sobre el problema de la redirección.
Pero todos los ejemplos anteriores tienen un problema común, ya que todos los métodos @ModelAttribute se ejecutan antes de que se ejecute el controlador, si el controlador devuelve una redirección, los datos del modelo se agregarán a la url como una cadena de consulta. Esto debe evitarse a toda costa, ya que podría revelar algunos secretos sobre cómo ha elaborado su aplicación.
Su solución sugerida (ver la parte 4 del blog) es usar un HandlerInterceptorAdapter
para hacer que los datos de referencia comunes sean visibles para la vista. Dado que los datos de referencia no deben estar estrechamente acoplados a los controladores, esto no debería representar un problema, en cuanto al diseño.
No hay buenas maneras de resolver este problema (es decir, sin crear componentes personalizados, sin cantidades excesivas de configuración xml explícita y sin instanciación manual de RedirectView
).
Puede instanciar RedirectView
manualmente a través de su constructor de 4 argumentos, o declarar el siguiente bean en su contexto (cerca de otros resolutores de vistas):
public class RedirectViewResolver implements ViewResolver, Ordered {
// Have a highest priority by default
private int order = Integer.MIN_VALUE;
// Uses this prefix to avoid interference with the default behaviour
public static final String REDIRECT_URL_PREFIX = "redirectWithoutModel:";
public View resolveViewName(String viewName, Locale arg1) throws Exception {
if (viewName.startsWith(REDIRECT_URL_PREFIX)) {
String redirectUrl = viewName.substring(REDIRECT_URL_PREFIX.length());
return new RedirectView(redirectUrl, true, true, false);
}
return null;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
No use @ModelAttribute
. Almacene los usuarios en el ModelMap
explícitamente. Estás haciendo tanto con el objeto de comando de todos modos.
@RequestMapping(method=RequestMethod.GET)
public String setupForm(ModelMap model){
// Set up command object
Intake intake = new Intake();
intake.setIntakeDate(new Date());
model.addAttribute("intake", intake);
model.addAttribute("users", users);
return "addIntake";
}
La desventaja de esto es si se produce un error de validación en addIntake()
. Si desea simplemente devolver el nombre lógico del formulario, también debe recordar volver a llenar el modelo con los usuarios, de lo contrario, el formulario no se configurará correctamente.
O bien, haga que la solicitud sea POST. Las solicitudes de obtención solo mostrarán los atributos del modelo como parámetros de solicitud que aparecen en la URL.
Sé que esta pregunta y respuesta es antigua, pero me encontré con ella después de tener problemas similares y no hay mucha otra información que pueda encontrar.
Creo que la respuesta aceptada no es muy buena. La respuesta justo debajo de ella por axtavt es mucho mejor. La pregunta no es si tiene sentido anotar los atributos del modelo en un controlador. Se trata de cómo emitir un redireccionamiento "limpio" desde un controlador que normalmente usa ModelAttributes. El controlador en sí mismo normalmente requiere los datos de referencia, pero a veces necesita redireccionar a otro lugar para condiciones excepcionales o lo que sea, y pasar los datos de referencia no tiene sentido. Creo que este es un patrón válido y común.
(Vaya, me encontré con este problema de forma inesperada con Tomcat. Los redireccionamientos simplemente no funcionaban y recibía mensajes de error como: java.lang.ArrayIndexOutOfBoundsException: 8192. Finalmente determiné que la longitud máxima predeterminada del encabezado de Tomcat es 8192. No lo hice t se da cuenta de que los Atributos del Modelo se estaban agregando automáticamente a la URL de redireccionamiento, y que causaba que la longitud del encabezado excediera la longitud máxima del encabezado de Tomcat).
crear manualmente un objeto RedirectView trabajado para mí:
@RequestMapping(method=RequestMethod.POST)
public ModelAndView addIntake(@ModelAttribute("intake")Intake intake, BindingResult result){
// Validate Intake command object and persist to database
// ...
String caseNumber = assignIntakeACaseNumber();
RedirectView rv = new RedirectView("redirect:intakeDetails.htm?caseNumber=" + caseNumber);
rv.setExposeModelAttributes(false);
return new ModelAndView(rv);
}
En mi humilde opinión esto debería ser el comportamiento predeterminado al redirigir
intente agregar el código siguiente en servlet-config.xml
<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" />
a veces esto resolverá el problema.
model.asMap().clear();
return "redirect:" + news.getUrl();
:)