0

I have a bunch of REST API endpoint which I'd like to change their base path.

I understand that using spring.data.rest.base-path=/XYZ in the application.properties only affect the endpoints which their classes are annotated with @BasePathAwareController. But it seems that the endpoint will be available both with the base path and without the base path !

This means that if I have /customer/add, making a call to /customer/add and /XYZ/customer/add both would work !

I'm wondering why this happens and if there is a way to easily add a base path to the desired endpoints. (I have static files to serve and I don't want them to have a base path)

I already tried custom REST MVC configuration:

import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.stereotype.Component;

@Component
public class CustomizedRestMvcConfiguration extends RepositoryRestConfigurerAdapter {

  @Override
  public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
    config.setBasePath("/api");
  }
}

But it didn't set the base path for the endpoints.

UPDATE 1 Here is my pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<groupId>com.boot</groupId>
<artifactId>myproj</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>myproj</name>
<url>http://maven.apache.org</url>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <start-class>com.boot.cut_costs.App</start-class>
    <java.version>1.8</java.version>
</properties>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.8.RELEASE</version>
</parent>

<!-- uncomment this if you see the "DerInputStream.getLength(): lengthTag=109, 
    too big." error again. <build> <resources> <resource> <directory>src/main/resources</directory> 
    <filtering>true</filtering> <excludes> <exclude>*.p12</exclude> </excludes> 
    </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> 
    <includes> <include>*.p12</include> </includes> </resource> </resources> 
    </build> -->
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
    <dependency>
        <groupId>org.javassist</groupId>
        <artifactId>javassist</artifactId>
        <version>3.18.0-GA</version><!--$NO-MVN-MAN-VER$ -->
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security.oauth</groupId>
        <artifactId>spring-security-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-configuration-processor</artifactId>
        <optional>true</optional>
    </dependency>
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.7.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-cas</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-acl</artifactId>
    </dependency>
    <dependency>
        <groupId>bsf</groupId>
        <artifactId>bsf</artifactId>
        <version>2.4.0</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-batch</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-ldap</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.jdo</groupId>
        <artifactId>jdo-api</artifactId>
        <version>3.0.1</version>
    </dependency>
    <dependency>
        <groupId>com.querydsl</groupId>
        <artifactId>querydsl-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>springloaded</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-stdlib</artifactId>
        <version>1.0.6</version>
    </dependency>
    <dependency>
        <groupId>org.scala-lang</groupId>
        <artifactId>scala-library</artifactId>
        <version>2.11.0</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
    <dependency>
        <groupId>commons-validator</groupId>
        <artifactId>commons-validator</artifactId>
        <version>1.6</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.5</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-jersey</artifactId>
    </dependency>
    <dependency>
        <groupId>org.modelmapper</groupId>
        <artifactId>modelmapper</artifactId>
        <version>0.7.5</version>
    </dependency>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.4</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.eclipse.jetty.aggregate</groupId>
        <artifactId>jetty-all</artifactId>
        <version>9.2.13.v20150730</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.integration</groupId>
        <artifactId>spring-integration-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.apache.hadoop</groupId>
        <artifactId>hadoop-core</artifactId>
        <version>1.0.0</version>
    </dependency>
    <dependency>
        <groupId>com.sleepycat</groupId>
        <artifactId>je</artifactId>
        <version>5.0.73</version>
    </dependency>
    <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-commons</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.hateoas</groupId>
        <artifactId>spring-hateoas</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.plugin</groupId>
        <artifactId>spring-plugin-core</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Here is my application.properties:

spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type=TRACE
logging.level.org.springframework.web=DEBUG

server.port=8443
server.ssl.enabled=false
#server.ssl.key-store=classpath:tomcat.keystore
#server.ssl.key-store-password=password
#server.ssl.key-password=password
#server.ssl.key-alias=cutcosts

spring.data.rest.base-path=api

Here is my REST endpoint:

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;
import javax.validation.ValidationException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.boot.cut_costs.dto.UserDetailsDto;
import com.boot.cut_costs.service.AuthenticationService;
import com.boot.cut_costs.service.CustomUserDetailsService;
import com.boot.cut_costs.validator.UserDetailsDtoValidator;

@RestController
public class AuthenticationController {

    @Autowired
    protected AuthenticationManager authenticationManager;

    @Autowired
    private CustomUserDetailsService userDetailsService;

    @Autowired
    private UserDetailsDtoValidator createUserDetailsDtoValidator;

    private Logger logger = LoggerFactory.getLogger(AuthenticationController.class);

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public void login(@RequestBody UserDetailsDto userDetailsDTO, HttpServletResponse response) throws IOException {
        logger.debug("Authenticating login attemp from user " + userDetailsDTO.getUsername());
        authenticateUserAndSetSession(userDetailsDTO.getUsername(), userDetailsDTO.getPassword(), response);
    }

    @RequestMapping(value = "/signup", method = RequestMethod.POST)
    public void create(@RequestBody UserDetailsDto userDetailsDTO, HttpServletResponse response, BindingResult result) {
        String username = userDetailsDTO.getUsername();
        String password = userDetailsDTO.getPassword();
        String name = userDetailsDTO.getName();
        logger.debug("User signup attempt with username: " + username);
        createUserDetailsDtoValidator.validate(userDetailsDTO, result);
        if (result.hasErrors()) {
            throw new ValidationException(result.getAllErrors().get(0).getCode());
        }
        userDetailsService.saveIfNotExists(username, password, name);
        authenticateUserAndSetSession(username, password, response);
        logger.debug("New UserDetails with username " + username + " was created");
    }

    private void authenticateUserAndSetSession(String username, String password, HttpServletResponse response) throws BadCredentialsException {
        logger.debug("Authentication user " + username);
        Authentication authenticatedUser = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        if (authenticatedUser != null) {
            AuthenticationService.addAuthentication(response, authenticatedUser.getName());
            SecurityContextHolder.getContext().setAuthentication(authenticatedUser);
        } else {
            logger.debug("Authentication failed for user " + username);
            throw new BadCredentialsException("Bad credentials provided");
        }
    }

}
Zico
  • 2,349
  • 2
  • 22
  • 25
Arian
  • 7,397
  • 21
  • 89
  • 177
  • Which spring version are you using, latest `Boot 1.5.8.RELEASE` sets the base path correctly without duplication. – 11thdimension Nov 22 '17 at 07:36
  • you can use the `@RequestMapping("/XYZ")` annotation on the desired controller that contains all endpoints you want. – Amr Alaa Nov 22 '17 at 07:51
  • @AmrAlaa it's not only one controller and I'd like to do it at one place. – Arian Nov 22 '17 at 17:40
  • @11thdimension I was using `@BasePathAwareController` on my classes. I removed it and updated the Spring to 1.5.8.RELEASE, and now I only get the endpoints without base path (i.e. `/api/`) – Arian Nov 23 '17 at 04:32
  • I have posted a sample that's working for me, please have a look. – 11thdimension Nov 23 '17 at 04:34

1 Answers1

2

Edit

Following property changes base path for Spring Data REST services

spring.data.rest.base-path=api

If you want to change base path for the normal controllers ie. @Controller, @RestController then use following property

server.context-path=api

This property won't affect @RepositoryRestResource (Spring Data REST) URLs. It does affect repository rest path.

Here's a sample with 1.5.8.RELEASE.

application.properties

spring.data.rest.base-path=api

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test</groupId>
    <artifactId>TestDataRest</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>TestDataRest</name>
    <description>TestDataRest</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

TestDataRestApplication.java

package com.test;

import javax.annotation.PostConstruct;
import javax.persistence.Entity;
import javax.persistence.Id;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@SpringBootApplication
public class TestDataRestApplication {

    @Autowired
    private PersonRepository repo;

    @PostConstruct
    public void inti () {
        System.out.println("************** running init");

        repo.save(new Person("Batman"));
    }

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

@RepositoryRestResource(path="person")
interface PersonRepository extends JpaRepository<Person, Long> {

}

@Entity
class Person {
    @Id
    private long id;

    private String name;

    public Person() {

    }

    public Person(String name) {
        super();
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
11thdimension
  • 10,333
  • 4
  • 33
  • 71
  • It didn't work. I updated my post with some more information and code from my project. – Arian Nov 23 '17 at 04:47
  • 2
    There's no `@RepositoryRestResource` annotated repository in the code you've posted. `spring.data.rest.base-path` property only works with the `Spring Data REST`, if you want to change base URL for the normal Controllers then you will have to use `server.context-path` – 11thdimension Nov 23 '17 at 05:08
  • Yep, it works now. You can post it as an answer and I accept it. – Arian Nov 23 '17 at 07:38
  • I noticed that it also affects the path for the static resources. Is there a way to configure it somehow that only the `@Controller`s get affected ? – Arian Nov 23 '17 at 17:31
  • `server.context-path` changes path for everything, it's kind of like WAR file name. If you need to set base path selectively for controllers and static resources, then check this answer https://stackoverflow.com/a/38228080/5343269 – 11thdimension Nov 24 '17 at 00:44