1

Basing on https://spring.io/guides/gs/accessing-data-rest/ in the sample project https://github.com/jcoig/gs-accessing-data-rest) i have repository defined as follows:

@RepositoryRestResource
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
    List<Person> findByLastName(@Param("name") String name);
}

Such defined repository is available via http://localhost:8080/persons and the response is:

{
  "_links" : {
    "self" : {
      "href" : "http://localhost:8080/persons{?page,size,sort}",
      "templated" : true
    },
    "search" : {
      "href" : "http://localhost:8080/persons/search"
    }
  },
  "_embedded" : {
    "persons" : [ {
      "firstName" : "John",
      "lastName" : "Smith",
      "_links" : {
        "self" : {
          "href" : "http://localhost:8080/persons/1"
        }
      }
    } ]
  },
  "page" : {
    "size" : 20,
    "totalElements" : 1,
    "totalPages" : 1,
    "number" : 0
  }
}

I don't want to have persons in the URL and I don't want to have persons as the key in the returned JSON. Of Course I can define my repository as below:

@RepositoryRestResource(collectionResourceRel = "key", path = "path")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
    List<Person> findByLastName(@Param("name") String name);
}

but my question is how to change default Spring's behavior and got custom key and custom path provider (just as example to disable s suffixes).

jcoig
  • 303
  • 1
  • 4
  • 8

2 Answers2

2

If the solution applying @Order(value = Ordered.HIGHEST_PRECEDENCE) on the customized RelProvider instance is not working the following workaround may help:

@Configuration
@Import(RepositoryRestMvcConfiguration.class)
public class RestMvcConfigurer extends RepositoryRestMvcConfiguration
{
...
@Override
public ResourceMappings resourceMappings()
{

  final Repositories repositories = repositories();
  final RepositoryRestConfiguration config = config();
  return new RepositoryResourceMappings( config, repositories, new YourCustomRelProvider());
}
}

Further I had to exclude evo-inflector from the classpath:

<dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
        <exclusions>
            <exclusion>
                <groupId>org.atteo</groupId>
                <artifactId>evo-inflector</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

Not really a nice solution, but it works for my setup.

Dominik
  • 131
  • 3
  • Thanks for reply Dominik! It works! Now i have `ccc` as a key in the response JSON with `http://localhost:8080/person{?page,size,sort}` in ‘href’. Remaining question is how to change this path without using ‘path’ param in the `@RepositoryRestResource` annotation. – jcoig Oct 15 '14 at 09:50
  • I am not sure what you mean by changing the path. If you refer to the base URI, you can overwrite the `protected void configureRepositoryRestConfiguration( final RepositoryRestConfiguration config )` method in `RestMvcConfigurer`. Therein call `config.setBaseUri("mybaseuri")`. – Dominik Nov 17 '14 at 16:34
  • Returned JSON contains word "persons" in two different places: as a key under `_embedded` and in the several hrefs. I want to change paths in those hrefs without specifing `path = "path"` in the `@RepositoryRestResource` annotation. – jcoig Nov 19 '14 at 09:27
1

Let's make sure what we're trying to achieve in the first place: Spring Data REST exposes two kinds of primary resources: collection resources and item resources. To be able to differentiate between the two we need two different relation names. So by default Spring Data REST uses the uncapitalized domain class name for the item resource rel and the Evo Inflector library to pluralize the item resource rel and use that as collection relation name (effectively what you informally described as adding the s suffix).

If you manually exclude the Evo Inflector library, Spring Data REST falls back to ${itemRel}List for the collection relation, which first of all is more clunky than using properly pluralized names.

As you also discovered already, you can manually configure the names to be used for each repository. However, it's a very bad idea to configure the collection resource rel to be the item resource rel as this will prevent the clients from differentiating between the two resource types.

Assuming you've got this far reading and still want to deploy a custom strategy to globally alter the relation names for types you can implement a RelProvider (see EvoInflectorRelProvider or DefaultRelProvider for implementation examples). If you register that implementation as Spring bean.

@Configuration
class MyConfig {

  @Bean
  YourCustomRelProvider customRelProvider() {
    return new YourCustomRelProvider(…);
  }
}

You might wanna experiment with the order (see @Order or the Ordered interface) of the implementation to make sure your custom provider is chosen in favor of the default ones registered.

Oliver Drotbohm
  • 80,157
  • 18
  • 225
  • 211
  • Thanks for reply! Unfortunetely I still can't get what I want… In the project from (without `collectionResourceRel` and `path`) I added a class implementing `RelProvider` with annotations `Component` and `Order(value = Ordered.HIGHEST_PRECEDENCE)` and three overridden methods: `getItemResourceRelFor` (returns `"iii"`), `getCollectionResourceRelFor` (returns `"ccc"`) and `supports` (returns `true`). In `Application.java` I added bean just like in Your answer and… I still got `persons` in the JSON's key and in the URL. What am I doing wrong? – jcoig Sep 29 '14 at 05:59
  • This is how we got things going and it seems to work. Did it so all page's have items so the page interface is normalized. Can you confirm your component is being registered as bean? (make sure your supports method is called) component annotation and bean method in a configuration is redundant...but should still work. We did NOT need to do the Order annotation. Ours is spring-boot too – Chris DaMour Oct 07 '14 at 19:52
  • Chris, please look at this project: https://github.com/jcoig/gs-accessing-data-rest. It's based on http://spring.io/guides/gs/accessing-data-rest/ with changes based on Oliver's hints (or as I understood them). – jcoig Oct 08 '14 at 06:02
  • indeed, it does not work as is, i've made an answer to show you how i got it to work. Some stuff i don't understand..like the RepositoryRelProvider has this annotation @Order(Ordered.LOWEST_PRECEDENCE + 10) .. LOWEST_PRECEDENCE is Integer.MAX_VALUE..so is it depending on overflow? that seems really weird! – Chris DaMour Oct 08 '14 at 17:07
  • The strangest thing..i downloaded sources and documentation for your project...and it just started working i now see the rel provider being stepped into and the relationship coming out is ccc!? no idea why it started working...i changed no code. – Chris DaMour Oct 08 '14 at 17:24
  • Chris, I can't see your answer which you mentioned in your comment… – jcoig Oct 09 '14 at 05:41
  • yes, because i ended up not needing to do anything different than the code you had...it just worked. – Chris DaMour Oct 13 '14 at 21:23