2010-01-16 14 views
14

Sto utilizzando Spring MVC (3.0) con i controller basati su annotazioni. Vorrei creare URL REST-ful per le risorse ed essere in grado di non richiedere (ma ancora facoltativamente consentire) l'estensione del file alla fine dell'URL (ma assumere il tipo di contenuto HTML se non l'estensione). Funziona immediatamente con Spring MVC finché non ci sono punti (punto/punto) nella parte del nome file.Cercando di creare URL REST-pieni con più punti nella parte "filename" - Spring 3.0 MVC

Tuttavia alcuni dei miei URL richiedono un identificatore con punti nel nome. Per esempio. in questo modo:

http://company.com/widgets/123.456.789.500 

In questo caso Primavera cerca un tipo di contenuto per l'estensione .500 e trova nessuno così errori. Sono in grado di utilizzare work-arounds come aggiungere .html alla fine, codificare l'identificatore o aggiungere una barra finale. Non sono contento di nessuno se questi, ma potrebbe probabilmente vivere con l'aggiunta di .html.

Ho cercato invano un modo per ignorare il rilevamento di estensione file predefinito in primavera.

È possibile personalizzare o disabilitare il rilevamento dell'estensione file per un determinato metodo controller o pattern URL, ecc.?

risposta

10

La corrispondenza del modello @PathVariable è un po 'nervosa quando si tratta di punti nell'URL (vedere SPR-5778). Puoi renderlo meno nervoso (ma più schizzinoso), e ottenere un controllo migliore sugli URL ricchi di punti, impostando la proprietà useDefaultSuffixPattern su DefaultAnnotationHandlerMapping su false.

Se non l'hai già esplicitamente dichiarata DefaultAnnotationHandlerMapping nel vostro contesto (e la maggior parte delle persone non farlo dal momento che è dichiarata implicitamente per voi), quindi è possibile aggiungere esplicitamente, e impostare la proprietà.

+0

Questo ha fatto il trucco, grazie Skaffman. Ma ha rotto un'altra parte dell'app ... il contentNegotiatingViewResolver che mappa un'estensione ".json" su Jackson non funziona più. Qualche idea su come risolvere questo problema? – nickdos

+0

Ordinato il problema con contentNegotiatingViewResolver associando esplicitamente il mio metodo controller all'URL incluso ".json". Per esempio. usando l'annotazione: @RequestMapping (value = "/search.json", method = RequestMethod.GET). C'è una soluzione migliore per questo? – nickdos

+0

Hai provato '@RequestMapping (value ="/search * ")'? – skaffman

12

Probabilmente, è un brutto scherzo, volevo solo esplorare l'estensibilità di Spring @MVC. Ecco un numero personalizzato PathMatcher. Utilizza lo $ nel modello come indicatore finale - se il modello termina con esso, il marcatore viene rimosso e il modello corrisponde al corrispondente predefinito, ma se il modello ha $ nel mezzo (ad esempio ...$.*), tale modello non corrisponde.

public class CustomPathMatcher implements PathMatcher { 
    private PathMatcher target; 

    public CustomPathMatcher() { 
     target = new AntPathMatcher(); 
    } 

    public String combine(String pattern1, String pattern2) { 
     return target.combine(pattern1, pattern2); 
    } 

    public String extractPathWithinPattern(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return ""; 
     } 
     return target.extractPathWithinPattern(pattern, path); 
    } 

    public Map<String, String> extractUriTemplateVariables(String pattern, 
      String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return Collections.emptyMap(); 
     } 
     return target.extractUriTemplateVariables(pattern, path); 
    } 

    public Comparator<String> getPatternComparator(String pattern) { 
     final Comparator<String> targetComparator = target.getPatternComparator(pattern); 
     return new Comparator<String>() { 
      public int compare(String o1, String o2) { 
       if (isEncoded(o1)) { 
        if (isEncoded(o2)) { 
         return 0; 
        } else { 
         return -1; 
        } 
       } else if (isEncoded(o2)) { 
        return 1; 
       } 
       return targetComparator.compare(o1, o2); 
      }   
     }; 
    } 

    public boolean isPattern(String pattern) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return true; 
     } 
     return target.isPattern(pattern); 
    } 

    public boolean match(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return false; 
     } 
     return target.match(pattern, path); 
    } 

    public boolean matchStart(String pattern, String path) { 
     if (isEncoded(pattern)) { 
      pattern = resolvePattern(pattern); 
      if (pattern == null) return false; 
     } 
     return target.match(pattern, path); 
    } 

    private boolean isEncoded(String pattern) { 
     return pattern != null && pattern.contains("$"); 
    } 

    private String resolvePattern(String pattern) { 
     int i = pattern.indexOf('$'); 
     if (i < 0) return pattern; 
     else if (i == pattern.length() - 1) { 
      return pattern.substring(0, i); 
     } else { 
      String tail = pattern.substring(i + 1); 
      if (tail.startsWith(".")) return null; 
      else return pattern.substring(0, i) + tail; 
     } 
    } 
} 

Config:

<bean id = "pathMatcher" class = "sample.CustomPathMatcher" /> 

<bean class = "org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> 
    <property name = "pathMatcher" ref="pathMatcher" /> 
</bean> 

<bean class = "org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name = "pathMatcher" ref="pathMatcher" /> 
</bean> 

e l'uso (dato "/hello/1.2.3", value è "1.2.3"):

@RequestMapping(value = "/hello/{value}$", method = RequestMethod.GET) 
public String hello(@PathVariable("value") String value, ModelMap model) 

EDIT:: Ora non si rompe la "barra finale non importa" regola

+0

Grazie per questo codice - davvero utile. Ho pensato di menzionare a chiunque altro che utilizza questo elemento che dovresti rimuovere dalla tua configurazione di mvc se aggiungi la configurazione personalizzata di pathMatcher sopra. –

3

Ho avuto lo stesso problema e anche io risolto da PathMatcher personalizzato. La mia soluzione è in qualche modo più semplice rispetto a quanto proposto da axtavt. Il mio PathMatcher ha anche un obiettivo AntPathMatcher finale privato, e delega tutte le chiamate ad esso invariato, fatta eccezione per la partita() metodo:

@Override 
public boolean match(String pattern, String path) { 
    return pattern.endsWith(".*") ? false : target.match(pattern, path); 
} 

Questo funziona perché la primavera cerca di far corrispondere i controller aggiungendo fino alla fine "." . Ad esempio, con mappatura del percorso "/ widgets/{id}" e URL "/widgets/1.2.3.4", Spring tenta la prima corrispondenza "/ widget/{id}." e successivamente "/ widget/{id} ". Il primo corrisponderà, ma lascerà solo "1.2.3" per id.

Il mio PatchMatcher rifiuta specificamente i pattern che terminano ". *", Quindi il primo tentativo fallisce e il secondo corrisponde.

Se si utilizza ContentNegotiatingViewResolver è ancora possibile specificare il tipo di contenuto nell'URL utilizzando il parametro di richiesta "format" (se il parametro favorParameter è impostato su true).

-jarppe

6
<!-- language: lang-java --> 

@Controller public class MyController { @RequestMapping(value="/widgets/{preDot}.{postDot}") public void getResource(@PathVariable String preDot, @PathVariable String postDot) { String fullPath = preDot + "." + postDot; //... } }

// codice di cui sopra dovrebbe corrispondere /widgets/111.222.333.444

+0

soluzione semplice e semplice – Jayz

1

Per aggiungere alla risposta di skaffman, se si sta utilizzando <mvc:annotation-driven/> e si desidera sovrascrivere il valore useDefaultSuffixPattern, è possibile sostituire il tag <mvc:annotation-driven> con il seguente:

<!-- Maps requests to @Controllers based on @RequestMapping("path") annotation values --> 
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> 
    <property name="order" value="1" /> 
    <property name="useDefaultSuffixPattern" value="false" /> 
</bean> 

<!-- Enables annotated @Controllers --> 
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter" /> 
4

Primavera 3.2 è cambiato, e suggerisce che si impostano le proprietà sul fagiolo RequestMappingHandlerMapping, in modo esplicito (se non si utilizza lo spazio dei nomi MVC) or by using a BeanPostProcessor come le seguenti (è necessario eseguire la scansione o istanziare):

@Component 
public class IncludeExtensionsInRequestParamPostProcessor implements BeanPostProcessor { 
    @Override 
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { 
     if (bean instanceof RequestMappingHandlerMapping) { 
      RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping)bean; 
      mapping.setUseRegisteredSuffixPatternMatch(false); 
      mapping.setUseSuffixPatternMatch(false); 
     } 
     return bean; 
    } 

    @Override 
    public Object postProcessAfterInitialization(Object bean, String beanName) { return bean; } 
} 

Puoi anche aggiungere lo :.* al tuo @RequestMapping, ad es. "/{documentPath:.*}"(see JIRA comment)

2

JFY: in primavera 4 questo problema viene risolto tramite: WebMvcConfigurerAdapter.

@Configuration 
class MvcConfiguration extends WebMvcConfigurerAdapter { 

@Override 
public void configurePathMatch(PathMatchConfigurer configurer) { 
    configurer.setUseSuffixPatternMatch(false); 
} 
} 

Oppure tramite WebMvcConfigurationSupport come here.

Problemi correlati