Jak ustawić podstawowy adres URL do odpoczynku w wiosennym rozruchu?

118

Próbuję połączyć mvc i resztę w jednym projekcie buta sprężynowego.

Chcę ustawić ścieżkę bazową dla wszystkich pozostałych kontrolerów (np. Example.com/api) w jednym miejscu (nie chcę @RequestMapping('api/products')zamiast tego przypisywać do każdego kontrolera adnotacji po prostu @RequestMapping('/products').

Dostęp do kontrolerów MVC powinien być możliwy pod adresem example.com/cokolwiek

Czy to możliwe?

(Nie używam sprężynowej podpórki danych, tylko sprężynowe MVC)

Teimuraz
źródło
spójrz na tę dokumentację. spring.io/spring-framework/docs/current/…
leeor
server.servlet.contextPath = / api
Daniel T. Sobrosa
Spring boot w wersji 2.1.4.RELEASE, spring.mvc.servlet.path = / api i server.servlet.context-path = / api, obie działają
Prayag Sharma
server.servlet.context-path = / api rozwiązanie jest przeznaczone dla APLIKACJI, a nie tylko dla REST. Dotyczy to również usług SOAP. Jeśli chcesz podzielić swoją ścieżkę usług SOAP i REST, powinieneś użyć @RequestMapping ('api / ...') ... medium.com/@bm.celalkartal/
bmck

Odpowiedzi:

89

W przypadku Spring Boot 1.2+ (<2.0) wystarczy jedna właściwość w application.properties:

spring.data.rest.basePath=/api

odnośnik: https://docs.spring.io/spring-data/rest/docs/current/reference/html/#getting-started.changing-base-uri

W przypadku wersji 2.x użyj

server.servlet.context-path=/api
Suroj
źródło
4
To jest dokładna odpowiedź, której udzielił Thorinkor.
Jean-François Beauchef
8
Dzięki, ale to nie działa dla mnie w wersji Spring Boot 1.5.7.RELEASE. Druga odpowiedź server.contextPath = / api działała
Jay
10
@Suroj To rozwiązanie działa tylko z kontrolerami z adnotacjami RepositoryRestController, a nie z RestController ...
Nano
jira.spring.io/browse/DATAREST-1211 W tym pakiecie Jira Ticket wspomniano, że jest to „spring.data.rest.base-path dla Spring Boot 2.0.0”. Niestety oba nie działają dla mnie.
Carsten Hagemann
6
dla SB 2+ jest to server.servlet.context-path = / url
vicky
96

Trochę późno, ale to samo pytanie przywiodło mnie tutaj, zanim dotarłem do odpowiedzi, więc zamieszczam je tutaj. Utwórz (jeśli nadal go nie masz) plik application.properties i dodaj

server.contextPath=/api

Więc w poprzednim przykładzie, jeśli masz RestController, @RequestMapping("/test")uzyskasz do niego dostęp jaklocalhost:8080/api/test/{your_rest_method}

źródło pytania: jak wybrać adres URL mojej aplikacji webowej rozruchowej wiosny

OriolBG
źródło
19
Jak wymusić to, aby działało tylko z RestControllers i uzyskać dostęp do zwykłych kontrolerów bez „/ api”
Siya Sosibo
@Stoan znalazłem rozwiązanie, sprawdź moją odpowiedź :-)
kravemir
Nie rób tego! Zobacz odpowiedź Thorinkora.
Stefan,
Odpowiedź Thorinkora dotyczy specjalnie Spring Data REST.
8
server.contextPath jest teraz przestarzała, użyj zamiast tego server.servlet.context-path
DS.
46

Do wersji szkieletowej do butów sprężynowych 2.0.4.RELEASE+. Dodaj tę linię doapplication.properties

server.servlet.context-path=/api
shellhub
źródło
1
Ma to również wpływ na folder publiczny :-(
Michel
5
to jest poprawna odpowiedź dla Spring boot 2+. spring.data.rest.basePathnie działa dla Spring boot 2
jackycflau
27

Ponieważ jest to pierwsze trafienie Google dotyczące problemu i zakładam, że więcej osób będzie tego szukało. Pojawiła się nowa opcja od wersji Spring Boot „1.4.0”. Można teraz zdefiniować niestandardowy RequestMappingHandlerMapping, który pozwala zdefiniować inną ścieżkę dla klas z adnotacją @RestController

Inną wersję z niestandardowymi adnotacjami, która łączy @RestController z @RequestMapping, można znaleźć w tym poście na blogu

@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();
                        if (AnnotationUtils.findAnnotation(beanType, RestController.class) != 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);
                    }
                };
            }
        };
    }
}
mh-dev
źródło
2
W Spring Boot 2.0.0+ możesz pracować bezpośrednio z interfejsu WebMvcRegistrations. WebMvcRegistrationsAdapter został usunięty na rzecz dodania domyślnych metod do interfejsu.
The Gilbert Arenas Dagger
27

Nie mogłem uwierzyć, jak skomplikowana jest odpowiedź na to pozornie proste pytanie. Oto kilka odniesień:

Należy wziąć pod uwagę wiele różnych kwestii:

  1. Ustawiając server.context-path=/apiw application.properties, możesz skonfigurować prefiks dla wszystkiego (jego server.context-path, a nie server.contextPath!)
  2. Kontrolery Spring Data z adnotacją @RepositoryRestController, które ujawniają repozytorium jako punkt końcowy reszty, będą używać zmiennej środowiskowej spring.data.rest.base-pathw application.properties. Ale zwykły @RestControllernie weźmie tego pod uwagę. Zgodnie z dokumentacją danych sprężynowych istnieje adnotacja @BasePathAwareController, której można użyć do tego. Ale mam problemy w związku z zabezpieczeniami Springa, kiedy próbuję zabezpieczyć taki kontroler. Już go nie znaleziono.

Innym obejściem jest prosta sztuczka. Nie możesz poprzedzić statycznego ciągu znaków w adnotacji, ale możesz użyć takich wyrażeń:

@RestController
public class PingController {

  /**
   * Simple is alive test
   * @return <pre>{"Hello":"World"}</pre>
   */
  @RequestMapping("${spring.data.rest.base-path}/_ping")
  public String isAlive() {
    return "{\"Hello\":\"World\"}";
  }
}
Robert
źródło
Jak byś włączył adnotację?
Teimuraz,
2
Eee, to zawsze musisz pamiętać o dodawaniu tego przedrostka za każdym razem, gdy tworzysz nowy kontroler
The Gilbert Arenas Dagger
13

W przypadku Boot 2.0.0+ działa to dla mnie: server.servlet.context-path = / api

Juan Carlos Vergara Santos
źródło
4
Wydaje się, że to wszystko umieściło pod / api, nie tylko mappers @RestController. Ale i tak dzięki. Twoje informacje są nadal przydatne.
eigil
9

Znalazłem czyste rozwiązanie, które dotyczy tylko odpoczynku kontrolerów.

@SpringBootApplication
public class WebApp extends SpringBootServletInitializer {

    @Autowired
    private ApplicationContext context;

    @Bean
    public ServletRegistrationBean restApi() {
        XmlWebApplicationContext applicationContext = new XmlWebApplicationContext();
        applicationContext.setParent(context);
        applicationContext.setConfigLocation("classpath:/META-INF/rest.xml");

        DispatcherServlet dispatcherServlet = new DispatcherServlet();
        dispatcherServlet.setApplicationContext(applicationContext);

        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(dispatcherServlet, "/rest/*");
        servletRegistrationBean.setName("restApi");

        return servletRegistrationBean;
    }

    static public void main(String[] args) throws Exception {
        SpringApplication.run(WebApp.class,args);
    }
}

Spring boot zarejestruje dwa serwlety dyspozytorskie - domyślnie dispatcherServletdla kontrolerów i restApidyspozytora dla @RestControllerszdefiniowanych w rest.xml:

2016-06-07 09:06:16.205  INFO 17270 --- [           main] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'restApi' to [/rest/*]
2016-06-07 09:06:16.206  INFO 17270 --- [           main] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]

Przykład rest.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <context:component-scan base-package="org.example.web.rest"/>
    <mvc:annotation-driven/>

    <!-- Configure to plugin JSON as request and response in method handler -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="jsonMessageConverter"/>
            </list>
        </property>
    </bean>

    <!-- Configure bean to convert JSON to POJO and vice versa -->
    <bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
    </bean>
</beans>

Ale nie jesteś ograniczony do :

  • użyj XmlWebApplicationContext, możesz użyć dowolnego innego dostępnego typu kontekstu, tj. AnnotationConfigWebApplicationContext, GenericWebApplicationContext, GroovyWebApplicationContext, ...
  • define jsonMessageConverter, messageConvertersfasola w kontekście spoczynku, mogą być zdefiniowane w kontekście nadrzędnym
kravemir
źródło
Czy można to zrobić programowo bez użycia XML?
Arian
@ArianHosseinzadeh Yes. Można to zrobić programowo. Istnieje wiele sposobów konfigurowania kontekstu sprężyny. W tym przykładzie pokazałem, jak stworzyć kontekst potomny do obsługi REST API. Po prostu naucz się konfigurować kontekst w Javie, a następnie połącz tę wiedzę z wiedzą w tej odpowiedzi. To się nazywa programowanie.
kravemir
7

Możesz utworzyć niestandardową adnotację dla swoich kontrolerów:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@RestController
@RequestMapping("/test")
public @interface MyRestController {
}

Użyj go zamiast zwykłego @RestController w klasach kontrolera i dodaj adnotacje za pomocą @RequestMapping.

Właśnie przetestowane - działa w Spring 4.2!

Ilya Novoseltsev
źródło
Dziękuję Ci. Próbowałem tego. Ale teraz muszę dodać adnotację do każdej metody za pomocą @RequestMapping ("/ products"), @RequestMapping ("/ products / {id}"). Zamiast tego potrzebuję adnotacji kontrolera z @RequestMapping ("/ products") i metod z @RequestMapping, @RequestMapping ("/: id"). A kontroler produktów powinien być dostępny w api / products (np. Ustaw prefiks api w jednym miejscu)
Teimuraz
2
W takim razie nie, nie ma rozwiązania po wyjęciu z pudełka, AFAIK. Możesz spróbować zaimplementować własne RequestMappingHandlerMapping. Spring Data REST ma mapper podobny do tego, czego potrzebujesz - BasePathAwareHandlerMapping.
Ilya Novoseltsev
@moreo, czy znalazłeś odpowiednie rozwiązanie? Byłbym szczęśliwy, gdybyś mógł wysłać to jako odpowiedź. mam tutaj te same wymagania.
fischermatte
@fischermatte, Nie, nie znalazłem dokładnie tego, czego szukałem, umieszczam @RequestMapping ("/ api / products") lub @RequestMapping ("/ api / users") przed każdą klasą kontrolera, a przed metodą po prostu kolejny @ RequestMapping („/ {id}”). Ale nie sądzę, żeby to był duży problem, jeśli chcę zmienić „api” na coś, po prostu zmienię to na początku każdej klasy.
Teimuraz
@IlyaNovoseltsev Jest rozwiązanie, zobacz moją odpowiedź :-)
kravemir
7

Mogę się trochę spóźnić, ALE ... Uważam, że to najlepsze rozwiązanie. Ustaw to w pliku application.yml (lub analogicznym pliku konfiguracyjnym):

spring:
    data:
        rest:
            basePath: /api

O ile pamiętam, to wszystko - wszystkie twoje repozytoria zostaną ujawnione pod tym identyfikatorem URI.

thorinkor
źródło
Czy możesz to trochę wyjaśnić lub wskazać ważną dokumentację?
Dmitry Serdiuk
1
Odpowiednie dokumenty są dostępne pod adresem docs.spring.io/spring-data/rest/docs/current/reference/html/… .
bruce szalwinski
11
zmienna środowiskowa spring.data.rest.base-path ma wpływ tylko na odpoczynek danych wiosennych i nienawiść wiosennych. Zwykły @RestController nadal będzie siedział w katalogu głównym!
Robert,
4
@thorinkor na podstawie tego, co mówisz, że w większości przypadków ludzie będą budować repozytoria Spring Data REST? A OP wyraźnie mówi, że ma kontrolerów odpoczynku ...
Jean-François Beauchef
1
Myślę, że zadziała tylko wtedy, gdy używasz SpringDataRest.
Jaumzera
6

Spróbuj użyć PathMatchConfigurer (Spring Boot 2.x):

@Configuration
public class WebMvcConfig implements WebMvcConfigurer  {

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.addPathPrefix("api", HandlerTypePredicate.forAnnotation(RestController.class));
    }
}
Harald Wendel
źródło
1
Dzięki, właśnie tego szukałem! Pozwala to ustawić element ścieżki kontekstu dla wszystkich RestControllerów skonfigurowanych za pomocą tego WebMvcConfig, podobnie jak w przypadku spring.data.rest.base-path.
Buurman,
Twoja odpowiedź jest na miejscu @HaraldWendel: +1: Możesz ją trochę ulepszyć, rozszerzając ją nieco, na przykład wyjaśniając dokładnie, co robi twój kod (tak jak próbowałem zrobić w moim komentarzu) i / lub może link do trochę javadoc lub dokumentacja opisująca to użycie.
Buurman
To jedyne rozwiązanie, które zadziałało, ponieważ używam interfejsów kontrolera
Anatoly Yakimchuk
4

Możesz utworzyć klasę bazową z @RequestMapping("rest")adnotacjami i rozszerzyć wszystkie inne klasy o tę klasę bazową.

@RequestMapping("rest")
public abstract class BaseController {}

Teraz wszystkie klasy, które rozszerzają tę klasę bazową, będą dostępne pod adresem rest/**.

Saket Mehta
źródło
3
To nie jest poprawna odpowiedź, użytkownik odwołuje się do adnotacji kontrolera. Jeśli rozszerzysz klasę abstrakcyjną za pomocą adnotacji RequestMapping, a nowa klasa ma również RequestMapping, ta ostatnia zastąpi pierwszą, nie połączy dwóch.
Massimo
Czy wiesz, że adnotacje nie są dziedziczone w Javie, chyba że odziedziczyła ona meta adnotację? Sprawdź to: stackoverflow.com/a/21409651 . Wydaje się, że @RequestMapping tego nie ma: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
Mashrur
3

Dla tych, którzy używają konfiguracji YAML (application.yaml).

Uwaga : to działa tylko dlaSpring Boot 2.x.x

server:
  servlet:
    contextPath: /api

Jeśli nadal używasz Spring Boot 1.x

server:
  contextPath: /api
Prasanth Rajendran
źródło
1

W przypadku spring-boot 2.x możesz skonfigurować w application.properties:

spring.mvc.servlet.path=/api
Bułgar Sadykow
źródło
1

server.servlet.context-path=/apibyłoby rozwiązaniem, jak sądzę. Miałem ten sam problem i to mnie rozwiązało. Użyłem server.context-path. Wydawało się jednak, że jest to przestarzałe i stwierdziłem, że server.servlet.context-pathteraz rozwiązuje problem. Innym rozwiązaniem, które znalazłem, było dodanie tagu podstawowego do moich stron w interfejsie (H5). Mam nadzieję, że to komuś pomoże.

Twoje zdrowie

Rahul Talreja
źródło
0

To rozwiązanie ma zastosowanie, jeśli:

  1. Chcesz przedrostek, RestControllerale nie Controller.
  2. Nie używasz Spring Data Rest.

    @Configuration
    public class WebConfig extends WebMvcConfigurationSupport {
    
    @Override
    protected RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
        return new ApiAwareRequestMappingHandlerMapping();
    }
    
    private static class ApiAwareRequestMappingHandlerMapping extends RequestMappingHandlerMapping {
    
        private static final String API_PATH_PREFIX = "api";
    
        @Override
        protected void registerHandlerMethod(Object handler, Method method, RequestMappingInfo mapping) {
            Class<?> beanType = method.getDeclaringClass();
            if (AnnotationUtils.findAnnotation(beanType, RestController.class) != null) {
                PatternsRequestCondition apiPattern = new PatternsRequestCondition(API_PATH_PREFIX)
                        .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);
        }
    }

    }

Jest to podobne do rozwiązania opublikowanego przez mh-dev, ale myślę, że jest trochę czystsze i powinno być obsługiwane w każdej wersji Spring Boot 1.4.0+, w tym 2.0.0+.

Sztylet Gilberta Arenasa
źródło
Jeśli używam Pageable w moim RestControler, api / coś nie daje mi żadnego podstawowego ani domyślnego konstruktora dla interfejsu org.springframework.data.domain.Pageable
K. Ayoub
0

Zgodnie z dokumentami REST Spring Data , jeśli używasz application.properties , użyj tej właściwości, aby ustawić ścieżkę podstawową:

spring.data.rest.basePath=/api

Pamiętaj jednak, że Spring używa luźnego wiązania , więc można użyć tej odmiany:

spring.data.rest.base-path=/api

... lub ten, jeśli wolisz:

spring.data.rest.base_path=/api

Jeśli używasz application.yml , użyjesz dwukropków jako separatorów kluczy:

spring:
  data:
    rest:
      basePath: /api

(Dla porównania, powiązany bilet został utworzony w marcu 2018 r., Aby wyjaśnić dokumenty.)

J Woodchuck
źródło
0

działał server.contextPath = / path

Pravin
źródło
0

Możesz utworzyć niestandardową adnotację dla swoich kontrolerów:

Użyj go zamiast zwykłego @RestController w klasach kontrolera i dodaj adnotacje za pomocą @RequestMapping.

Działa dobrze na wiosnę 4.2!

Prerit Neema
źródło