2016-01-14 10 views
11

So che è possibile impostare il server.contextPath in application.properties per modificare il contesto di root.Come configurare un prefisso URI @RestController predefinito per tutti i controller?

Inoltre, posso aggiungere un contesto aggiuntivo nella configurazione dell'applicazione per la primavera di avvio, come nel seguente esempio (in Groovy) per aggiungere un "/ api" per le mappature URL del contesto root:

@Bean 
ServletRegistrationBean dispatcherServlet() { 
    ServletRegistrationBean reg = new ServletRegistrationBean(new DispatcherServlet(), "/") 
     reg.name = "dispatcherServlet" 
     reg.addInitParameter("contextConfigLocation", "") 
     reg.addUrlMappings("/api/*") 
     reg.loadOnStartup = 2 
     reg 
    } 
} 

Sto cercando di avere un URI di base separato "/ api" specifico per le chiamate al servizio web, che posso sfruttare per la sicurezza, ecc. Tuttavia l'utilizzo dell'approccio precedente significa che qualsiasi mio URI, servizio web o meno, può essere raggiunto con "/" o "/ api" e non fornisce alcuna segregazione concreta.

Qualcuno è a conoscenza di un approccio migliore per impostare un percorso di base per tutti @RestController (s) utilizzando la configurazione, senza dover formalmente prefixare ogni controller con/api /? Se sono obbligato a precedere manualmente l'URI per ciascun controller, sarebbe possibile ometterlo erroneamente e bypassare le mie misure di sicurezza specifiche per i servizi web.

Qui è un riferimento in Stack Overflow per lo stesso tipo di domanda, che non fu mai del tutto risolta:

Spring Boot: Configure a url prefix for RestControllers

+0

Un biglietto è stato aperto su Jira per questo con un lavoro in giro: https://jira.spring.io/browse/SPR-13882 – pczeus

risposta

1

Qualcuno ha depositato un problema nel Spring MVC Jira e trovare una bella soluzione , che sto usando ora. L'idea è di utilizzare Spring Language di espressione nel prefisso inserito in ciascun file RestController e fare riferimento a una singola proprietà nel file application.properties Spring Boot.

Ecco il link del problema: https://jira.spring.io/browse/SPR-13882

+1

Grazie per aver pubblicato un riferimento al biglietto Jira Olivier. Avrei dovuto postarlo prima. Avevo fatto la stessa domanda sui forum degli sviluppatori di Springs e mi è stato chiesto di aprire il ticket, cosa che ho fatto. Non sono sicuro che sia un'ottima soluzione, ma sarà sufficiente. – pczeus

+0

Sfortunatamente, questo è ora rotto quando uso Spring HATEOAS. Quando uso il framework per generare il link, raccoglie Spring EL e interpreta il '{'/'}' come variabili di percorso ... Si interrompe qui: https://github.com/spring-projects/spring- quadro/blob/v4.2.4.RELEASE/primavera-web/src/main/java/org/spring framework/web/util/UriComponents.java # L327 –

3

Ho avuto lo stesso problema e non ero un fan dell'opzione EL primavera a causa dei problemi documentati e volevo il prefisso da strettamente controllato nei controllori, ma Non volevo dipendere dagli sviluppatori che facevano la cosa giusta.

Potrebbe esserci un modo migliore in questi giorni, ma questo è quello che ho fatto. Ragazzi, potete vedere alcuni aspetti negativi, sono ancora in procinto di testare gli effetti collaterali.

  1. Definire un'annotazione personalizzata.
    Ciò consente a uno sviluppatore di fornire esplicitamente attributi tipizzati come int apiVersion(), String resourceName(). Questi valori sarebbero la base del prefisso più tardi.
  2. controllori di riposo annotati con questa nuova annotazione
  3. implementato un RequestMappingHandlerMapping personalizzato

Nel RequestMappingHandlerMapping, ho potuto leggere l'attributo dell'annotazione personalizzato e modificare il finale RequestMappingInfo come avevo bisogno. Ecco alcuni frammenti di codice:

@Configuration 
public class MyWebMvcConfigurationSupport extends WebMvcConfigurationSupport { 

    @Bean 
    public RequestMappingHandlerMapping requestMappingHandlerMapping() { 
     return new MyCustomRequestMappingHandlerMapping(); 
    } 
} 

E nel MyCustomRequestMappingHandlerMapping, sovrascrivere il registerHandlerMethod:

private class MyCustomRequestMappingHandlerMapping extends RequestMappingHandlerMapping { 

    private Logger myLogger = LoggerFactory.getLogger(MyCustomRequestMappingHandlerMapping.class); 

    public MyCustomRequestMappingHandlerMapping() { 
     super(); 
    } 

    @Override 
    protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { 

     // find the class declaring this method 
     Class<?> beanType = method.getDeclaringClass(); 

     // check for the My rest controller annotation 
     MyRestController myRestAnnotation = beanType.getAnnotation(MyRestController.class); 

     if (myRestAnnotation != null) { 
      // this is a My annotated rest service, lets modify the URL mapping 

      PatternsRequestCondition oldPattern = mapping.getPatternsCondition(); 

      // create a pattern such as /api/v${apiVersion}/${resourceName} 
      String urlPattern = String.format("/api/v%d/%s", 
        myRestAnnotation.apiVersion(), 
        myRestAnnotation.resourceName()); 

      // create a new condition 
      PatternsRequestCondition apiPattern = 
        new PatternsRequestCondition(urlPattern); 

      // ask our condition to be the core, but import all settinsg from the old 
      // pattern 
      PatternsRequestCondition updatedFinalPattern = apiPattern.combine(oldPattern); 

      myLogger.info("re-writing mapping for {}, myRestAnnotation={}, original={}, final={}", 
        beanType, myRestAnnotation, oldPattern, updatedFinalPattern); 

      mapping = new RequestMappingInfo(
        mapping.getName(), 
        updatedFinalPattern, 
        mapping.getMethodsCondition(), 
        mapping.getParamsCondition(), 
        mapping.getHeadersCondition(), 
        mapping.getConsumesCondition(), 
        mapping.getProducesCondition(), 
        mapping.getCustomCondition() 
        ); 
     } 

     super.registerHandlerMethod(handler, method, mapping); 
    } 
} 
14

C'è una nuova soluzione per risolvere questo tipo di problema disponibile dalla primavera Boot 1.4.0.RC1 (Dettagli vedi https://github.com/spring-projects/spring-boot/issues/5004)

La soluzione di Shahin ASkari disabilita parti della configurazione automatica, quindi potrebbe causare altri problemi.

La seguente soluzione prende la sua idea e la integra correttamente nello stivale a molla. Per il mio caso ho voluto tutti i RestControllers con l'API percorso di base, ma ancora servire contenuti statici con il percorso principale (fe webapp angolare)

Edit: ho riassunto in su in un post sul blog con una versione leggermente migliorata vedere https://mhdevelopment.wordpress.com/2016/10/03/spring-restcontroller-specific-basepath/

@Configuration 
public class WebConfig { 

    @Bean 
    public WebMvcRegistrationsAdapter webMvcRegistrationsHandlerMapping() { 
     return new WebMvcRegistrationsAdapter() { 
      @Override 
      public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { 
       return new RequestMappingHandlerMapping() { 
        private final static String API_BASE_PATH = "api"; 

        @Override 
        protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) { 
         Class<?> beanType = method.getDeclaringClass(); 
         RestController restApiController = beanType.getAnnotation(RestController.class); 
         if (restApiController != null) { 
          PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_BASE_PATH) 
            .combine(mapping.getPatternsCondition()); 

          mapping = new RequestMappingInfo(mapping.getName(), apiPattern, 
            mapping.getMethodsCondition(), mapping.getParamsCondition(), 
            mapping.getHeadersCondition(), mapping.getConsumesCondition(), 
            mapping.getProducesCondition(), mapping.getCustomCondition()); 
         } 

         super.registerHandlerMethod(handler, method, mapping); 
        } 
       }; 
      } 
     }; 
    } 

} 
3

soluzione un po 'meno prolissa che non duplicare la logica del controllo l'annotazione, ma cambia solo il percorso di mappatura:

private static final String API_PREFIX = "api"; 

@Bean 
WebMvcRegistrationsAdapter restPrefixAppender() { 
    return new WebMvcRegistrationsAdapter() { 
     @Override 
     public RequestMappingHandlerMapping getRequestMappingHandlerMapping() { 
      return new RequestMappingHandlerMapping() { 
       @Override 
       protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) { 
        RequestMappingInfo mappingForMethod = super.getMappingForMethod(method, handlerType); 
        if (mappingForMethod != null) { 
         return RequestMappingInfo.paths(API_PREFIX).build().combine(mappingForMethod); 
        } else { 
         return null; 
        } 
       } 
      }; 
     } 
    }; 
} 

effetti indesiderati,

Il controller di errore verrà mappato anche in/api/error, che interrompe la gestione degli errori (DispatcherServlet continuerà a reindirizzare gli errori a/error senza prefisso!).

La soluzione possibile è saltare il percorso/errore quando si aggiunge il prefisso/api nel codice sopra (un altro "se").