2010-01-29 19 views
56

utilizando Spring MVC 3.0.0.RELEASE, tengo el siguiente controlador:¿Cómo evito que las variables Spring 3.0 MVC @ModelAttribute aparezcan en la URL?

@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 admisión de un objeto de comando poblada de un formulario HTML, valida el objeto de comando, persiste la información a la base de datos y devuelve un número de caso

Todo funciona muy bien, a excepción de cuando vuelvo a dirigir a la página intakeDetails.htm, consigo una URL que tiene este aspecto:

http://localhost:8080/project/intakeDetails.htm?caseNumber=1&users=user1&users=user2&users=user3&users=user4...

¿Cómo evito la Colección de usuario aparezcan en el URL?

+3

Una solución sencilla para esto se ha escrito en los comentarios de la respuesta de axtavt, echar un vistazo en eso si quieres una solución de una sola línea. –

+0

también es cierto para 'Spring 4.x' – Andremoniy

Respuesta

28

Desde la primavera de 3,1 la RequestMappingHandlerAdapter proporciona una bandera llamado ignoreDefaultModelOnRedirect que puede utilizar para evitar el uso del contenido del modelo defautl si el controlador redirige.

+3

es decir '' mvc: anotado por la acción ignoreDefaultModelOnRedirect = "true" /> ' – Gareth

+0

Esta es ciertamente la respuesta correcta ahora. Si no desea que el modelo se complete para el redireccionamiento, simplemente desactívelo. – David

+0

¡Esta debería ser la respuesta aceptada! – spike07

18

No hay formas adecuadas 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).

Usted puede crear una instancia RedirectView manualmente a través de su constructor 4-argumento, o declarar la siguiente frijol en su contexto (cerca de otros resolución de vista):

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

Fui con la solución de 4 argumentos RedirectView: return new ModelAndView (new RedirectView (" intakeDetails.htm? CaseNumber = "+ caseNumber, false, true, false)); –

+0

Gracias por resumir que en los comentarios, copiar y pegar de ese comentario funcionó muy bien! –

0

No utilice @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 que si un error de validación tiene lugar 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.

+1

Esta solución no funcionará para los datos a los que TODAS las vistas deben tener acceso (que es exactamente para lo que '@ModelAttribute es para) – cdeszaq

18

La anotación de método @ModelAttribute está destinada a exposing reference data para la capa de vista. 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, hay una entrada blog que trata sobre el problema de la redirección.

Pero todos los ejemplos anteriores tienen un problema común , a medida que se ejecutan todos los métodos @ModelAttribute antes de que se ejecute el controlador , si el manejador devuelve un redirigir los datos del modelo se añadirán a la URL como una cadena de consulta. Este debe evitarse a toda costa, ya que podría exponer algunos secretos sobre cómo ha configurado su aplicación.

Su solución sugerida (ver la parte 4 del blog) es utilizar 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.

+12

Wow, arquitecto buzzword bingo hecho en una palabra: HandlerInterceptorAdapter! –

+0

¡¡Lo mismo aquí !! : D –

+2

@RobertGrant Buzzzz [AbstractAnnotationConfigDispatcherServletInitializer] (http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/support/AbstractAnnotationConfigDispatcherServletInitializer.html) –

5

Sé que esta pregunta y respuesta es antigua, pero me encontré con ella después de tener problemas similares a mí mismo 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.

(Fwiw, 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 sabía que se añadían los ModelAttributes automáticamente a la URL de redireccionamiento, y que estaba causando la longitud de la cabecera a cabecera superior a la longitud máxima de Tomcat.)

38
model.asMap().clear(); 
return "redirect:" + news.getUrl(); 

:)

+2

Hay un montón , soluciones más complicadas presentadas en esta página, pero esta solución resuelve el problema con la menor cantidad de problemas. Añadiría que deberá agregar el parámetro Model a su método addIntake ... 'public String addIntake (@ModelAttribute (" intake ") Entrada de producto, modelo modelo, resultado BindingResult)' –

+0

¡Simple y eficiente! Si solo pudiera votar más de una vez ... – tibo

+0

@sideways: la solución más simple disponible, ¡gracias por compartir! –

2

En mi solicitud I Don No tengo ningún caso de uso para exponer los atributos del modelo en redirección, así que extendí org.springframework.web.se rvlet.view.UrlBasedViewResolver reemplazar el método createView y usados ​​declaró en el contexto de 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> 
2

Creación manual de un objeto RedirectView trabajó 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); 
} 

mi humilde opinión, esto debe ser el comportamiento predeterminado al redirigir

0

Hay una solución si ayuda a su causa.

 @ModelAttribute("users") 
     public Collection<String> getUsers(){ 
      return this.users; 
     } 

Aquí lo has hecho volver 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. Simplemente haga que sea como -

 @ModelAttribute("users") 
     public Collection<User> getUsers(){ 
      return this.users; 
     } 
+2

sucede con enumeraciones también –

+0

De hecho, tengo este problema para una propiedad de cadena, lo he envuelto en una clase Wrapper (obviamente) y el problema desapareció. –

1

O haga que solicite un mensaje POST. Las solicitudes de obtención solo mostrarán los atributos del modelo como parámetros de solicitud que aparecen en la URL.

5

I implementarse una variante de Sid's answer con menos copiar y pegar involucrados:

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 un bean de vista de resolución de ser definidos:

<bean id="viewResolver" class="com.example.RedirectsNotExposingModelUrlBasedViewResolver"> 
    <property name="viewClass" value="org.springframework.web.servlet.view.tiles2.TilesView"/> 
</bean> 
+0

esta es una solución más sutil a este problema que pude encontrar, no estoy seguro por qué califico tan bajo – parxier

0

intento de añadir más adelante en el código servlet-config.xml

<mvc:annotation-driven ignoreDefaultModelOnRedirect="true" /> 

veces esto va a resolver el problema.

1

Aquí es cómo hacerlo con una configuración basada en Java (Primavera 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 

} 
Cuestiones relacionadas