diff --git a/doc/validation.md b/doc/validation.md new file mode 100644 index 000000000..555b87d36 --- /dev/null +++ b/doc/validation.md @@ -0,0 +1,41 @@ +# Validation in Spring Data REST + +Integrating validation with the Spring Data REST Exporter is as easy as simply defining an instance of a [Validator](http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/validation/Validator.html). There is an ApplicationListener that that looks for these Validator instances on startup and wires them to the correct RepositoryEvent based on the bean name. + +For example, to validate entities before they are saved to the Repository, you only need to define a Validator instance in your ApplicationContext with a name that starts with "beforeSave". + + + + +All the events dicussed in [Handling ApplicationEvents in the REST Exporter](wiki/Handling-ApplicationEvents-in-the-REST-Exporter) can be validated. + +If any errors are found during validation, a [RepositoryConstraintViolationException](blob/master/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/RepositoryConstraintViolationException.java) will be thrown, resulting in a 400 Bad Request. + +### Advanced Configuration + +If you need a little more control over how the Validators are wired, you can instantiate a [ValidatingRepositoryEventListener](blob/master/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java) yourself and use a Map of Validators to their event names: + + + + + + + + + + + + diff --git a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java index 965c31f39..05005f3c9 100644 --- a/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java +++ b/spring-data-rest-repository/src/main/java/org/springframework/data/rest/repository/context/ValidatingRepositoryEventListener.java @@ -7,6 +7,7 @@ import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.data.rest.repository.RepositoryConstraintViolationException; import org.springframework.data.rest.repository.ValidationErrors; @@ -30,18 +31,20 @@ public class ValidatingRepositoryEventListener @Override public void afterPropertiesSet() throws Exception { if (validators.size() == 0) { - Map validators = applicationContext.getBeansOfType(Validator.class); - for (Map.Entry entry : validators.entrySet()) { - String name = entry.getKey(); + for (Map.Entry entry : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, + Validator.class) + .entrySet()) { + String name = null; Validator v = entry.getValue(); - if (name.contains("Save")) { - name = name.substring(0, name.indexOf("Save") + 4); - } else if (name.contains("Delete")) { - name = name.substring(0, name.indexOf("Delete") + 6); + if (entry.getKey().contains("Save")) { + name = entry.getKey().substring(0, name.indexOf("Save") + 4); + } else if (entry.getKey().contains("Delete")) { + name = entry.getKey().substring(0, name.indexOf("Delete") + 6); + } + if (null != name) { + this.validators.put(name, v); } - - this.validators.put(name, v); } } } diff --git a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java index a94a53956..b1f3bc7e8 100644 --- a/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java +++ b/spring-data-rest-webmvc/src/main/java/org/springframework/data/rest/webmvc/RepositoryRestController.java @@ -53,7 +53,6 @@ import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.server.ServerHttpRequest; @@ -68,7 +67,6 @@ import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.context.request.WebRequest; import org.springframework.web.util.UriComponentsBuilder; @@ -251,7 +249,7 @@ public class RepositoryRestController RepositoryMetadata repoMeta = repositoryMetadataFor(repository); Links links = new Links(); - Iterator iter = ((CrudRepository) repoMeta.repository()).findAll().iterator(); + Iterator iter = repoMeta.repository().findAll().iterator(); while (iter.hasNext()) { Object o = iter.next(); Serializable id = (Serializable) repoMeta.entityMetadata().idAttribute().get(o); @@ -396,7 +394,7 @@ public class RepositoryRestController URI baseUri = uriBuilder.build().toUri(); RepositoryMetadata repoMeta = repositoryMetadataFor(repository); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); MediaType incomingMediaType = request.getHeaders().getContentType(); final Object incoming = readIncoming(request, incomingMediaType, repoMeta.entityMetadata().type()); if (null == incoming) { @@ -441,7 +439,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); Object entity = repo.findOne(serId); if (null == entity) { model.addAttribute(STATUS, HttpStatus.NOT_FOUND); @@ -500,7 +498,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); Object entity = null; Class domainType = repoMeta.entityMetadata().type(); switch (request.getMethod()) { @@ -561,7 +559,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); if (null != eventPublisher) { eventPublisher.publishEvent(new BeforeDeleteEvent(serId)); @@ -596,7 +594,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); Object entity = repo.findOne(serId); if (null == entity) { model.addAttribute(STATUS, HttpStatus.NOT_FOUND); @@ -680,7 +678,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); final Object entity = repo.findOne(serId); if (null == entity) { model.addAttribute(STATUS, HttpStatus.NOT_FOUND); @@ -790,7 +788,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); final Object entity = repo.findOne(serId); if (null == entity) { model.addAttribute(STATUS, HttpStatus.NOT_FOUND); @@ -838,7 +836,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); final Object entity = repo.findOne(serId); if (null != entity) { AttributeMetadata attrMeta = repoMeta.entityMetadata().attribute(property); @@ -891,7 +889,7 @@ public class RepositoryRestController (Class) repoMeta.entityMetadata() .idAttribute() .type()); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); final Object entity = repo.findOne(serId); if (null == entity) { model.addAttribute(STATUS, HttpStatus.NOT_FOUND); @@ -901,7 +899,7 @@ public class RepositoryRestController // Find linked entity RepositoryMetadata linkedRepoMeta = repositoryMetadataFor(attrMeta); if (null != linkedRepoMeta) { - CrudRepository linkedRepo = (CrudRepository) linkedRepoMeta.repository(); + CrudRepository linkedRepo = linkedRepoMeta.repository(); Serializable sChildId = stringToSerializable(linkedId, (Class) linkedRepoMeta.entityMetadata() .idAttribute() @@ -950,13 +948,15 @@ public class RepositoryRestController @SuppressWarnings({"unchecked"}) @ExceptionHandler(OptimisticLockingFailureException.class) - @ResponseBody - public ResponseEntity handleLockingFailure(OptimisticLockingFailureException ex) throws IOException { - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); + public Model handleLockingFailure(OptimisticLockingFailureException ex) throws IOException { + Model model = new ExtendedModelMap(); + model.addAttribute(STATUS, HttpStatus.CONFLICT); + Map m = new HashMap(); m.put("message", ex.getMessage()); - return new ResponseEntity(objectMapper.writeValueAsBytes(m), headers, HttpStatus.CONFLICT); + + model.addAttribute(RESOURCE, m); + return model; } @SuppressWarnings({"unchecked"}) @@ -1013,7 +1013,7 @@ public class RepositoryRestController String sId = UriUtils.path(uris.get(1)); RepositoryMetadata repoMeta = repositoryMetadataFor(repoName); - CrudRepository repo = (CrudRepository) repoMeta.repository(); + CrudRepository repo = repoMeta.repository(); if (null == repo) { return null; } @@ -1057,18 +1057,14 @@ public class RepositoryRestController } } + List links = (List) entityDto.get(LINKS); + if (null == links) { + links = new ArrayList(); + entityDto.put(LINKS, links); + } for (String attrName : entityMetadata.linkedAttributes().keySet()) { - URI uri = UriComponentsBuilder.fromUri(baseUri) - .pathSegment(attrName) - .build() - .toUri(); - Link l = new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, uri); - List links = (List) entityDto.get(LINKS); - if (null == links) { - links = new ArrayList(); - entityDto.put(LINKS, links); - } - links.add(l); + links.add(new SimpleLink(repoRel + "." + entity.getClass().getSimpleName() + "." + attrName, + buildUri(baseUri, attrName))); } return entityDto;