Following through on that idea, I rewrote essentially the same service here using Spring Data REST.
From what I've seen so far, Spring Data REST helps you in two different ways. It relieves you of having to write a controller, at least for basic CRUD methods, and it also enables Hypermedia As The Engine Of Application State (HATEOAS).
When I started trying to get my Spring MVC Test Framework integration tests working again, the first thing I noticed was the following test failing:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
private MediaType jsonContentType = new MediaType(MediaType.APPLICATION_JSON.getType(), | |
MediaType.APPLICATION_JSON.getSubtype(), Charset.forName("utf8")); | |
. | |
. | |
. | |
.andExpect(content().contentType(jsonContentType)) | |
java.lang.AssertionError: Content type expected:<application/json;charset=UTF-8> but was:<application/hal+json;charset=UTF-8> |
Spring defines these in the MediaType class. They don't seem to have one of those for application/hal+json. I considered just not checking the mime type... But I googled a bit more and found Configuring Spring Data REST and Basic settings for Spring Data REST in the documentation. I used the following configuration to change the default mime type of the JSON responses back to application/json:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Bean | |
public RepositoryRestConfigurer repositoryRestConfigurer() { | |
return new RepositoryRestConfigurerAdapter() { | |
@Override | |
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) { | |
config.setDefaultMediaType(MediaType.APPLICATION_JSON); | |
} | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@RequestMapping(value = "/pokemon/{name}", produces = "application/json", method = RequestMethod.GET) | |
@ResponseStatus(HttpStatus.OK) | |
public Pokemon getPokemon(@PathVariable(value = "name") String name) { | |
Pokemon result = pokemonRepository.findByName(name); | |
if (result == null) { | |
throw new PokemonNotFoundException(name); | |
} | |
return result; | |
} |
But I'm not writing the controllers any more... Spring Data Rest creates a 'GET' endpoint taking an 'id' value, and it also creates one for my findByName() method in the Spring Data JPA repository interface. Here's my tests for these two cases:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@Test | |
public void readPokemonById() throws Exception { | |
String pokemonIdString = this.pokemonList.get(0).getId().toString(); | |
mockMvc.perform(get("/pokemons/" + pokemonIdString)) | |
.andExpect(status().isOk()) | |
.andExpect(content().contentType(jsonContentType)) | |
.andExpect(jsonPath("$.type1", is("psychic"))) | |
.andExpect(jsonPath("$.height", is(20))) | |
.andExpect(jsonPath("$.weight", is(1220))); | |
} | |
@Test | |
public void readPokemonByName() throws Exception { | |
mockMvc.perform(get("/pokemons/search/findByName?name=" + pokemonName)) | |
.andExpect(status().isOk()) | |
.andExpect(content().contentType(jsonContentType)) | |
.andExpect(jsonPath("$.type1", is("psychic"))) | |
.andExpect(jsonPath("$.height", is(20))) | |
.andExpect(jsonPath("$.weight", is(1220))); | |
} |
I rewrote the remaining tests to just use the id instead of the name.
One other change was needed... since Spring is now automatically generating a REST endpoint from the findByName() method in the repository interface, instead of me just calling it from the controller method, I needed to give that an annotation to instruct it how to handle the HTTP request parameter as its input:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Pokemon findByName(@Param("name") String name); |
Spring Data REST strikes me as a huge win for green field development situations where you just drop a dependency in the POM file (or Gradle build file) and, whoosh, instant REST endpoints for all the domain classes. It might be more problematic to use this in a legacy app where the Spring Data REST endpoints might not match the conventions of the existing endpoints. Also, the HATEOAS stuff, yeah, it's the semantic web wave of the future, but it is a bit different and might take some getting used to if this is your first exposure to it.