Comment définir l'URL de base pour le repos dans Spring Boot?

118

J'essaie de mélanger mvc et me reposer dans un seul projet de démarrage à ressort.

Je veux définir le chemin de base pour tous les contrôleurs de repos (par exemple, example.com/api) en un seul endroit (je ne veux pas annoter chaque contrôleur avec @RequestMapping('api/products'), à la place, juste @RequestMapping('/products').

Les contrôleurs MVC doivent être accessibles par example.com/whatever

C'est possible?

(Je n'utilise pas de repos de données à ressort, juste à ressort mvc)

Teimuraz
la source
server.servlet.contextPath = / api
Daniel T. Sobrosa
spring boot version 2.1.4.RELEASE, spring.mvc.servlet.path = / api et server.servlet.context-path = / api, les deux fonctionnent
Prayag Sharma
server.servlet.context-path = / api solution est pour APPLICATION, pas seulement pour REST. Il est également valable pour les services SOAP. Si vous souhaitez séparer votre chemin d'accès aux services SOAP et REST, vous devez utiliser @RequestMapping ('api / ...') ... medium.com/@bm.celalkartal
bmck

Réponses:

89

Avec Spring Boot 1.2+ (<2.0), il suffit d'une seule propriété dans application.properties:

spring.data.rest.basePath=/api

lien de référence: https://docs.spring.io/spring-data/rest/docs/current/reference/html/#getting-started.changing-base-uri

Pour 2.x, utilisez

server.servlet.context-path=/api
Suroj
la source
4
C'est la réponse exacte que Thorinkor a donnée.
Jean-François Beauchef
8
Merci, mais cela ne fonctionne pas pour moi dans la version Spring Boot v1.5.7.RELEASE. L'autre réponse server.contextPath = / api a fonctionné
Jay
10
@Suroj Cette solution fonctionne uniquement avec les contrôleurs annotés RepositoryRestController, pas avec RestController ...
Nano
jira.spring.io/browse/DATAREST-1211 Ce ticket Jira mentionne qu'il s'agit de "spring.data.rest.base-path pour Spring Boot 2.0.0". Malheureusement, les deux ne fonctionnent pas pour moi.
Carsten Hagemann
6
pour SB 2+ c'est server.servlet.context-path = / url
vicky
96

Un peu tard mais la même question m'a amené ici avant d'arriver à la réponse alors je la poste ici. Créez (si vous ne l'avez toujours pas) une application.properties et ajoutez

server.contextPath=/api

Donc, dans l'exemple précédent, si vous avez un RestController, @RequestMapping("/test")vous y accéderez commelocalhost:8080/api/test/{your_rest_method}

source de la question: comment choisir l'URL de mon application Web Spring Boot

OriolBG
la source
19
Comment faire pour que cela fonctionne uniquement avec RestControllers et accéder aux contrôleurs normaux sans le "/ api"
Siya Sosibo
@Stoan j'ai trouvé une solution, vérifiez ma réponse :-)
kravemir
Ne fais pas ça! Voir la réponse de Thorinkor.
Stefan
La réponse de Thorinkor est spécifiquement pour Spring Data REST.
8
server.contextPath est désormais obsolète, utilisez à la place server.servlet.context-path
DS.
46

Pour la version du framework Spring Boot 2.0.4.RELEASE+. Ajouter cette ligne àapplication.properties

server.servlet.context-path=/api
shellhub
la source
1
Cela affecte également le dossier public :-(
Michel
5
c'est la bonne réponse pour Spring boot 2+. spring.data.rest.basePathne fonctionne pas pour Spring Boot 2
jackycflau
27

Comme il s'agit du premier hit google pour le problème et je suppose que plus de gens le rechercheront. Il existe une nouvelle option depuis Spring Boot '1.4.0'. Il est désormais possible de définir un RequestMappingHandlerMapping personnalisé qui permet de définir un chemin différent pour les classes annotées avec @RestController

Une version différente avec des annotations personnalisées qui combine @RestController avec @RequestMapping peut être trouvée dans ce billet de blog

@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
la source
2
Dans Spring Boot 2.0.0+, travaillez directement depuis l'interface WebMvcRegistrations. WebMvcRegistrationsAdapter a été supprimé en faveur de l'ajout de méthodes par défaut à l'interface.
The Gilbert Arenas Dagger
27

Je ne pouvais pas croire à quel point la réponse à cette question apparemment simple est compliquée. Voici quelques références:

Il y a beaucoup de choses différentes à considérer:

  1. En définissant server.context-path=/apidans, application.propertiesvous pouvez configurer un préfixe pour tout (son chemin server.context et non server.contextPath!)
  2. Les contrôleurs Spring Data annotés avec @RepositoryRestController qui exposent un référentiel en tant que point de terminaison de repos utiliseront la variable d'environnement spring.data.rest.base-pathdans application.properties. Mais tout simplement @RestControllerne prendra pas cela en compte. Selon la documentation du repos de données de printemps, il existe une annotation @BasePathAwareControllerque vous pouvez utiliser pour cela. Mais j'ai des problèmes avec Spring-security lorsque j'essaye de sécuriser un tel contrôleur. On ne le trouve plus.

Une autre solution de contournement est une astuce simple. Vous ne pouvez pas préfixer une chaîne statique dans une annotation, mais vous pouvez utiliser des expressions comme celle-ci:

@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
la source
Comment vous mettriez-vous dans Annotation?
Teimuraz
2
meh, alors vous devez toujours vous rappeler d'ajouter ce préfixe chaque fois que vous créez un nouveau contrôleur
The Gilbert Arenas Dagger
13

Pour Boot 2.0.0+, cela fonctionne pour moi: server.servlet.context-path = / api

Juan Carlos Vergara Santos
la source
4
Cela a tout mis sous / api, il semble, pas seulement les mappeurs @RestController. Mais merci quand même. Vos informations sont toujours utiles.
eigil
9

J'ai trouvé une solution propre, qui n'affecte que les contrôleurs de repos.

@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 enregistrera deux servlets de dispatcher - par défaut dispatcherServletpour les contrôleurs, et restApidispatcher pour @RestControllersdéfini dans 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 [/]

L'exemple 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>

Mais, vous n'êtes pas limité à :

  • use XmlWebApplicationContext, vous pouvez utiliser tout autre type de contexte disponible, ie. AnnotationConfigWebApplicationContext, GenericWebApplicationContext, GroovyWebApplicationContext, ...
  • définir jsonMessageConverter, les messageConvertersbeans en contexte de repos, ils peuvent être définis dans le contexte parent
kravémir
la source
Est-il possible de le faire par programme sans utiliser le XML?
Arian
@ArianHosseinzadeh Oui. Il est possible de le faire par programme. Il existe de nombreuses façons de configurer le contexte de printemps. Dans l'exemple, j'ai montré comment créer un contexte enfant pour la gestion de l'API REST. Apprenez simplement à configurer le contexte dans Java, puis combinez ces connaissances avec des connaissances dans cette réponse. Cela s'appelle la programmation.
kravemir
7

Vous pouvez créer une annotation personnalisée pour vos contrôleurs:

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

Utilisez-le à la place du @RestController habituel sur vos classes de contrôleur et annotez les méthodes avec @RequestMapping.

Juste testé - fonctionne au printemps 4.2!

Ilya Novoseltsev
la source
Je vous remercie. J'ai essayé ça. Mais maintenant, je dois annoter chaque méthode avec @RequestMapping ("/ products"), @RequestMapping ("/ products / {id}"). Au lieu de cela, j'ai besoin d'annoter Controller avec @RequestMapping ("/ products") et des méthodes avec @RequestMapping, @RequestMapping ("/: id"). Et le contrôleur de produits doit être accessible à api / products (par exemple, définir le préfixe de l'API en un seul endroit)
Teimuraz
2
Dans ce cas, non, il n'y a pas de solution prête à l'emploi, AFAIK. Vous pouvez essayer de mettre en œuvre le vôtre RequestMappingHandlerMapping. Spring Data REST a un mappeur similaire à ce dont vous avez besoin - BasePathAwareHandlerMapping.
Ilya Novoseltsev
@moreo, avez-vous trouvé une solution appropriée? Je serais heureux si vous pouviez l'afficher comme réponse. j'ai la même exigence ici.
fischermatte
@fischermatte, Non, je n'ai pas trouvé exactement ce que je voulais, je place @RequestMapping ("/ api / products") ou @RequestMapping ("/ api / users") avant chaque classe de contrôleur et ensuite, avant la méthode juste une autre @ RequestMapping ("/ {id}"). Mais je ne pense pas que ce soit un gros problème, si je veux changer "api" en quelque chose, je le changerai juste au début de chaque cours.
Teimuraz
@IlyaNovoseltsev Il y a une solution, voir ma réponse :-)
kravemir
7

Je suis peut-être un peu en retard, MAIS ... je pense que c'est la meilleure solution. Configurez-le dans votre application.yml (ou fichier de configuration analogique):

spring:
    data:
        rest:
            basePath: /api

Si je me souviens bien, c'est tout - tous vos référentiels seront exposés sous cet URI.

thorinkor
la source
Pouvez-vous expliquer cela un peu ou pointer vers une documentation pertinente?
Dmitry Serdiuk
1
Les documents pertinents se trouvent sur docs.spring.io/spring-data/rest/docs/current/reference/html/… .
bruce szalwinski
11
la variable d'environnement spring.data.rest.base-path n'affecte que le repos des données du printemps et les hateoas du printemps. Plain @RestController restera assis à la racine!
Robert
4
@thorinkor basé sur ce que vous dites que dans la plupart des cas, les gens créeront des référentiels Spring Data REST? Et l'OP dit clairement qu'il a des contrôleurs de repos ...
Jean-François Beauchef
1
Je pense que cela ne fonctionnera que si vous utilisez SpringDataRest.
Jaumzera
6

Essayez d'utiliser un 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
la source
1
Merci, c'était exactement ce que je cherchais! Cela vous permet de définir un élément de chemin de contexte pour tous les RestControllers configurés via ce WebMvcConfig, similaire à ce que fait spring.data.rest.base-path.
Buurman
Votre réponse est parfaite sur @HaraldWendel: +1: Vous pouvez l'améliorer un peu plus en développant un peu plus, comme expliquer ce que votre code fait exactement (comme j'ai essayé de le faire dans mon commentaire) et / ou peut-être un lien vers un javadoc ou une documentation décrivant cette utilisation.
Buurman
C'est la seule solution qui a fonctionné pour moi car j'utilise des interfaces de contrôleur
Anatoly Yakimchuk
4

Vous pouvez créer une classe de base avec des @RequestMapping("rest")annotations et étendre toutes vos autres classes avec cette classe de base.

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

Désormais, toutes les classes qui étendent cette classe de base seront accessibles sur rest/**.

Saket Mehta
la source
3
Ce n'est pas la bonne réponse, l'utilisateur fait référence à l'annotation du contrôleur. Si vous étendez une classe abstraite avec une annotation RequestMapping et que la nouvelle classe a également un RequestMapping, ce dernier remplacera le premier, il ne concaténera pas les deux.
Massimo
Savez-vous que les annotations ne sont pas héritées dans java sauf si elles ont hérité de la méta-annotation? Vérifiez ceci: stackoverflow.com/a/21409651 . Et @RequestMapping ne semble pas avoir ça: docs.spring.io/spring-framework/docs/current/javadoc-api/org/…
Mashrur
3

Pour ceux qui utilisent la configuration YAML (application.yaml).

Remarque : cela ne fonctionne que pourSpring Boot 2.x.x

server:
  servlet:
    contextPath: /api

Si vous utilisez toujours Spring Boot 1.x

server:
  contextPath: /api
Prasanth Rajendran
la source
1

Avec spring-boot 2.x, vous pouvez configurer dans application.properties:

spring.mvc.servlet.path=/api
Bulgare Sadykov
la source
1

server.servlet.context-path=/apiserait la solution je suppose. J'ai eu le même problème et cela m'a résolu. J'ai utilisé server.context-path. Cependant, cela semblait être obsolète et j'ai trouvé que cela server.servlet.context-pathrésout le problème maintenant. Une autre solution de contournement que j'ai trouvée a été d'ajouter une balise de base à mes pages frontales (H5). J'espère que cela aide quelqu'un là-bas.

À votre santé

Rahul Talreja
la source
0

Cette solution s'applique si:

  1. Vous voulez préfixer RestControllermais pas Controller.
  2. Vous n'utilisez pas 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);
        }
    }

    }

Ceci est similaire à la solution publiée par mh-dev, mais je pense que c'est un peu plus propre et que cela devrait être pris en charge sur n'importe quelle version de Spring Boot 1.4.0+, y compris 2.0.0+.

Le poignard Gilbert Arenas
la source
Si j'utilise Pageable dans mon RestControler, api / quelque chose me donne Aucun constructeur principal ou par défaut trouvé pour l'interface org.springframework.data.domain.Pageable
K. Ayoub
0

Par documentation REST Spring Data , si vous utilisez application.properties , utilisez cette propriété pour définir votre chemin de base:

spring.data.rest.basePath=/api

Mais notez que Spring utilise une reliure détendue , donc cette variante peut être utilisée:

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

... ou celui-ci si vous préférez:

spring.data.rest.base_path=/api

Si vous utilisez application.yml , vous utiliseriez deux points pour les séparateurs de clé:

spring:
  data:
    rest:
      basePath: /api

(Pour référence, un ticket associé a été créé en mars 2018 pour clarifier les documents.)

J Marmotte
la source
0

a travaillé server.contextPath = / chemin

Pravin
la source
0

Vous pouvez créer une annotation personnalisée pour vos contrôleurs:

Utilisez-le à la place du @RestController habituel sur vos classes de contrôleur et annotez les méthodes avec @RequestMapping.

Fonctionne bien au printemps 4.2!

Prerit Neema
la source