2011-01-06 21 views
45

Se ho un RequestMapping in un controller primavera in questo modo ...Primavera 3.0 MVC vincolante Enums Case Sensitive

@RequestMapping(method = RequestMethod.GET, value = "{product}") 
public ModelAndView getPage(@PathVariable Product product) 

e di prodotto è un enum. per esempio. Product.Home

Quando ho richiedere la pagina, mysite.com/home

ottengo

Unable to convert value "home" from type 'java.lang.String' to type 'domain.model.product.Product'; nested exception is java.lang.IllegalArgumentException: No enum const class domain.model.product.Product.home 

c'è un modo per avere il convertitore tipo enum a capire che minuscolo casa è in realtà casa ?

Mi piacerebbe mantenere l'url case insensitive e le mie enumerazioni Java con lettere maiuscole standard.

Grazie

Soluzione

public class ProductEnumConverter extends PropertyEditorSupport 
{ 
    @Override public void setAsText(final String text) throws IllegalArgumentException 
    { 
     setValue(Product.valueOf(WordUtils.capitalizeFully(text.trim()))); 
    } 
} 

registrandolo

<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"> 
     <property name="customEditors"> 
      <map> 
       <entry key="domain.model.product.Product" value="domain.infrastructure.ProductEnumConverter"/> 
      </map> 
     </property> 
    </bean> 

Aggiungi ai controllori che hanno bisogno di conversione speciale

@InitBinder 
public void initBinder(WebDataBinder binder) 
{ 
    binder.registerCustomEditor(Product.class, new ProductEnumConverter()); 
} 
+1

ci sono classi 'RelaxedConversionService 'e' StringToEnumIgnoringCaseConverterFactory' in Spring Boot, ma non sono pubblici. – OrangeDog

risposta

21

In generale, si vuole creare un nuovo PropertyEditor che fa la normalizzazione per voi, e poi registrare che nel vostro controller in questo modo:

@InitBinder 
public void initBinder(WebDataBinder binder) { 

    binder.registerCustomEditor(Product.class, 
    new CaseInsensitivePropertyEditor()); 
} 
+1

Non voglio dover aggiungere questo ad ogni controller che utilizza questo enum. C'è una soluzione globale? –

+0

@dom farr: Esiste, ma sembra che skaffman mi abbia battuto su di esso. Guarda la sua risposta. – GaryF

+5

Per utenti di Spring 3.2; ora puoi usare '@ ControllerAdvice' per registrare i raccoglitori di init globali (tra le altre cose). Vedi [la guida di riferimento] (http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/new-in-3.2.html#new-in-3.2-webmvc-controller -advice) per ulteriori informazioni. – tmbrggmn

16

penso che si dovrà implement a Custom PropertyEditor.

Qualcosa di simile a questo:

public class ProductEditor extends PropertyEditorSupport{ 

    @Override 
    public void setAsText(final String text){ 
     setValue(Product.valueOf(text.toUpperCase())); 
    } 

} 

Vedi GaryF's answer su come legarlo

Ecco una versione più tollerante nel caso in cui si utilizza lettere minuscole nelle costanti enum (che probabilmente non dovrebbe, ma ancora):

@Override 
public void setAsText(final String text){ 
    Product product = null; 
    for(final Product candidate : Product.values()){ 
     if(candidate.name().equalsIgnoreCase(text)){ 
      product = candidate; 
      break; 
     } 
    } 
    setValue(product); 
} 
+0

Sembra che GenericConversionService.convert interferisca con questo tipo di soluzione. –

+0

@dom farr: sì, se usi 'ConversionService' avrai anche bisogno di [implementare un convertitore personalizzato] (http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html /validation.html#core-convert). –

+1

Sì, è quello che ho provato prima. Semplicemente non ha funzionato, quindi sono venuto qui e ho fatto la domanda. Forse non l'ho registrato correttamente? Ho usato il ConversionServiceFactoryBean, ma come ho detto, non andare. –

7

per aggiungere a @ di GaryF risposta, e di affrontare il tuo commento ad esso, è possibile dichiarare editor di proprietà personalizzati globali iniettando loro in un personalizzato AnnotationMethodHandlerAdapter. Spring MVC registra normalmente uno di questi per impostazione predefinita, ma puoi dargli uno appositamente configurato se lo desideri, ad es.

<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
    <property name="webBindingInitializer"> 
    <bean class="org.springframework.web.bind.support.ConfigurableWebBindingInitializer"> 
     <property name="propertyEditorRegistrars"> 
     <list> 
      <bean class="com.xyz.MyPropertyEditorRegistrar"/> 
     </list> 
     </property> 
    </bean> 
    </property> 
</bean> 

MyPropertyEditorRegistrar è un'istanza di PropertyEditorRegistrar, che a sua volta registra personalizzati PropertyEditor oggetti con Spring.

Dichiarare semplicemente che questo dovrebbe essere sufficiente.

+0

@skaffman. Non riesco a farlo funzionare se rimuovo il singolo initBinder in tutti i miei controller. Continuerò a scavare su questo però. Grazie –

+0

@dom: L'approccio funziona, lo uso da solo, ma potrei avere alcune delle specifiche errate – skaffman

+1

@skaffman. Forse questo è il mio problema: https://jira.springsource.org/browse/SPR-7077 –

11

E 'anche possibile creare un convertitore generico che funziona con tutte le enumerazioni come questo:

public class CaseInsensitiveConverter<T extends Enum<T>> extends PropertyEditorSupport { 

    private final Class<T> typeParameterClass; 

    public CaseInsensitiveConverter(Class<T> typeParameterClass) { 
     super(); 
     this.typeParameterClass = typeParameterClass; 
    } 

    @Override 
    public void setAsText(final String text) throws IllegalArgumentException { 
     String upper = text.toUpperCase(); // or something more robust 
     T value = T.valueOf(typeParameterClass, upper); 
     setValue(value); 
    } 
} 

Usage:

@InitBinder 
public void initBinder(WebDataBinder binder) { 
    binder.registerCustomEditor(MyEnum.class, new CaseInsensitiveConverter<>(MyEnum.class)); 
} 

O a livello mondiale come skaffman spiega

+0

Bello, mi piace la classe generica! Potrebbe non essere altrettanto efficiente, ma preferirei fare un equalsIgnoreCase invece di toUpperCase + valueOf. getEnumConstants dovrebbe essere disponibile al di fuori del campo typeParameterClass. Andando a combinare questa implementazione con il metodo da @SeanPatrickFloyd – Ryan

+0

In secondo luogo, iterare le costanti di enumerazione su ogni richiesta è troppo doloroso. Inserirò una HashMap all'interno di CaseInsensitiveConverter per accelerare i mapping. – Ryan

Problemi correlati