6

I know this might feel like a duplicate of this.
When to use @RestController vs @RepositoryRestResource
But I have a few things which were not addressed in that question.

  1. With @RepositoryRestResource, every method is by default exposed. Which I feel is a bit annoying. Correct me if I am wrong here. For example in the below case

    @RepositoryRestResource  
    public interface ProductRepository extends MongoRepository<Product, String> {} 
    

If I want only findAll() and findOne() to be exposed and not any other methods, especially delete. To achieve this I need to do something like this

    @RepositoryRestResource
    public interface ProductRepository extends MongoRepository<Product, String> {
      @RestResource(exported = false)
      @Override
      default void delete(String s) {
      }
      @RestResource(exported = false)
      @Override
      default void delete(Product product) {
      }
      @RestResource(exported = false)
      @Override
      default void delete(Iterable<? extends Product> iterable) {
      }
      @RestResource(exported = false)
      @Override
      default void deleteAll() {
      }
    }

Which I feel is really lot of unwanted code. This is much better to do with Rest Controller approach

  1. I believe it is better to return any value from REST endpoints using ResponseEntity. But with spring-data-rest approach, I am not sure how to do it.

  2. I could not find any way to unit test(Not IT) REST endpoints exposed by the RepositoryRestResource. But with REST controller approach, I can test my REST endpoints using MockServletContext, MockMvc, MockMvcBuilders

Given all these, is it still advantageous to use sping-data-rest(except for HATEOS)?
please clarify

Community
  • 1
  • 1
pvpkiran
  • 25,582
  • 8
  • 87
  • 134
  • 1
    I think it would be better to split your points into different questions. – aux Mar 24 '17 at 08:04
  • Hi, You mean one question for each of these points? Thanks – pvpkiran Mar 24 '17 at 10:29
  • Yes, usually it's much easier to find answers to direct questions on SO. This also helps others who search for similar topics. – aux Mar 26 '17 at 20:50

1 Answers1

19

Spring-data-rest is about providing REST endpoints for data repositories and it does provides solid REST with all bells and whistles including ALPS metadata, search endpoints, etc. This usually covers most use cases and provides basis for customisations.

Here're some hints.

Regarding p.1) - Customising exported resources and methods.

You do not need to put @RestResource(exported = false) on all delete(...) methods because only one is actually exported: void delete(Product entity). Look into relevant documentation chapter and the source code. If i do not miss something, you just need to provide these:

  • findAll(Pageable)
  • findOne(id)
  • save(Entity)
  • delete(Entity)

A note about exported repository methods. Sometimes it's easier to extend a very basic (empty) Repository<Product, String> repository interface and provide only methods you allow on the repository, for example:

@RepositoryRestResource
public interface ProductRepository extends Repository<Product, String> {
  long count();
  Page<Product> findAll(Pageable pageable);
  Product findOne(String entity);
  <S extends Product> S save(S entity);
}

Regarding a custom controller. To customise a default behaviour the easiest is to annotate controllers with @RespositoryRestController. Check-out docs and look into RepositoryEntityController.java - that's the default controller.

Regarding p.2) Returning ResponseEntity from controllers

It's very straingforward. You can wrap entity into Resource<T> (e.g. using a PersistentEntityResourceAssembler) and create a ResponseEntity with it. See RepositoryEntityController.java and some examples, like spring-restbucks.

Regarding p.3) - Testing rest endpoints

REST endpoints that expose the RepositoryRestResource are implemented in the RepositoryEntityController (part of the spring-data-rest).

If you implement your own custom controller, you can add unit tests as usual but things get more complex if you use PersistentEntityResourceAssembler.

Unit test example:

public class FooControllerTests {

  @Mock
  PersistentEntityResourceAssembler assembler;

  @Mock
  PersistentEntityResourceAssemblerArgumentResolver assemblerResolver;

  @Mock
  PersistentEntity<Foo, ?> entity;

  @InjectMocks
  FooController fooController;

  @Mock
  FooService fooService;

  private MockMvc mockMvc;

  @Rule
  public MockitoRule rule = MockitoJUnit.rule();

  @Before
  public void setup() {
    this.mockMvc = MockMvcBuilders.standaloneSetup(fooController)
        .setCustomArgumentResolvers(assemblerResolver)
        .build();
  }

  @Test
  public void test_GetItem_Success() throws Exception {
    final Foo foo = new Foo();
    
    when(fooService.findOne(1)).thenReturn(foo);
    when(assemblerResolver.supportsParameter(any())).thenReturn(true);
    when(assemblerResolver.resolveArgument(any(), any(), any(), any())).thenReturn(assembler);
    when(assembler.toResource(foo))
        .thenReturn(PersistentEntityResource.build(foo, entity).build());
    
    this.mockMvc.perform(get("/foo/1")).andExpect(status().isOk());
  }
}

See also "Building REST services with Spring" tutorial.

Hope this helps.

Taekwondavide
  • 219
  • 1
  • 3
  • 12
aux
  • 1,589
  • 12
  • 20
  • Hi aux, thanks for the detailed explanation. Just a couple of follow ups. Regarding point 2 and 3. In Point 2 What I meant is returning ResponseEntity from Repository(RepositoryRestResource) not Controller class. One of the key things in using spring-data-rest is avoiding writing controllers. Returning ResponseEntity from controller is pretty straight forward. But If I have to write controller myself then why would I use spring-data-rest. Regarding point 3 if you look at my question, I have categorically mentioned unit tests and Not IT(integration tests) Thanks – pvpkiran Mar 23 '17 at 19:21
  • p2. Returning ResponseEntity from a Repository would be odd solution. ResponseEntity represents the HTTP response. – aux Mar 24 '17 at 00:02
  • The spring-data-rest provides you with solid default implementation of REST API for your data and possibilities to extend and customise it. No need to write standard CRUD controllers, etc. Only if the standard impl is not enough then you start writing a custom controller as the last resort. Mostly you use other means of customisation. If you have really special needs for request processing, probably you have to write some sample code and evaluate it for your project. – aux Mar 24 '17 at 00:08
  • P2. I completely agree returning ResponseEntity from Repository would be a odd solution. But I always understood that returning ResponeEntity is a good thing from a REST endpoints. So keeping that in mind, using spring-data-rest defeats the purpose. isn't it? Please correct me if I am wrong. – pvpkiran Mar 24 '17 at 10:32
  • P3. Ya true, unit testing spring-data-rest endpoints will be redundant since the endpoints are generated by the framework. But the reason I was thinking of writing unit tests is to improve code coverage. Thanks – pvpkiran Mar 24 '17 at 10:34
  • P2. There must be some confusion. The spring-data-rest provides endpoints. Using ResponseEntity or some other response - it does not matter. It's not always "good thing" to return ResponseEntity - it just depends on what you need. You can search for questions like "When to return ResponseEntity from controller method?", e.g. this one: http://stackoverflow.com/questions/26549379/when-use-responseentityt-and-restcontroller-for-spring-restful-applications. – aux Mar 24 '17 at 13:39