-
-
Save wvuong/5673644 to your computer and use it in GitHub Desktop.
package com.willvuong.foodie.controller; | |
import com.willvuong.foodie.dao.PlaceRepository; | |
import com.willvuong.foodie.domain.Place; | |
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.data.repository.CrudRepository; | |
import org.springframework.stereotype.Controller; | |
import org.springframework.web.bind.annotation.RequestMapping; | |
@Controller | |
@RequestMapping("/rest/places") | |
public class PlacesRESTController extends RESTController<Place, String> { | |
@Autowired | |
public PlacesRESTController(PlaceRepository repo) { | |
super(repo); | |
} | |
} |
package com.willvuong.foodie.controller; | |
import com.google.common.base.Throwables; | |
import com.google.common.collect.Lists; | |
import com.google.common.collect.Maps; | |
import org.apache.commons.beanutils.BeanUtils; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.data.repository.CrudRepository; | |
import org.springframework.http.MediaType; | |
import org.springframework.web.bind.annotation.*; | |
import java.io.Serializable; | |
import java.util.List; | |
import java.util.Map; | |
public abstract class RESTController<T, ID extends Serializable> { | |
private Logger logger = LoggerFactory.getLogger(RESTController.class); | |
private CrudRepository<T, ID> repo; | |
public RESTController(CrudRepository<T, ID> repo) { | |
this.repo = repo; | |
} | |
@RequestMapping | |
public @ResponseBody List<T> listAll() { | |
Iterable<T> all = this.repo.findAll(); | |
return Lists.newArrayList(all); | |
} | |
@RequestMapping(method=RequestMethod.POST, consumes={MediaType.APPLICATION_JSON_VALUE}) | |
public @ResponseBody Map<String, Object> create(@RequestBody T json) { | |
logger.debug("create() with body {} of type {}", json, json.getClass()); | |
T created = this.repo.save(json); | |
Map<String, Object> m = Maps.newHashMap(); | |
m.put("success", true); | |
m.put("created", created); | |
return m; | |
} | |
@RequestMapping(value="/{id}", method=RequestMethod.GET) | |
public @ResponseBody T get(@PathVariable ID id) { | |
return this.repo.findOne(id); | |
} | |
@RequestMapping(value="/{id}", method=RequestMethod.POST, consumes={MediaType.APPLICATION_JSON_VALUE}) | |
public @ResponseBody Map<String, Object> update(@PathVariable ID id, @RequestBody T json) { | |
logger.debug("update() of id#{} with body {}", id, json); | |
logger.debug("T json is of type {}", json.getClass()); | |
T entity = this.repo.findOne(id); | |
try { | |
BeanUtils.copyProperties(entity, json); | |
} | |
catch (Exception e) { | |
logger.warn("while copying properties", e); | |
throw Throwables.propagate(e); | |
} | |
logger.debug("merged entity: {}", entity); | |
T updated = this.repo.save(entity); | |
logger.debug("updated enitity: {}", updated); | |
Map<String, Object> m = Maps.newHashMap(); | |
m.put("success", true); | |
m.put("id", id); | |
m.put("updated", updated); | |
return m; | |
} | |
@RequestMapping(value="/{id}", method=RequestMethod.DELETE) | |
public @ResponseBody Map<String, Object> delete(@PathVariable ID id) { | |
this.repo.delete(id); | |
Map<String, Object> m = Maps.newHashMap(); | |
m.put("success", true); | |
return m; | |
} | |
} |
Can you place an example that how we can implement parent-child relationships with this generic Rest Controller?
I love you, sir !
awesome! great!!
really good :)
thank you :)
gold, was looking for something like this for days
RESTController<Place, String> ?
ID String?
Hi wvuong, Thanks for sharing this, really useful for me as I am also implementing the similar thing in my project. Could you please share the entire code?
Wily helpful, it will help me to solve a problem that i was facing for a while.
Awesome sir. Kudo
thank you :)
Shouldn't there be ResourceNotFoundExceptions thrown when the entity doesn't exist whenever you do a repo.findOne?
Thank you! This stayed awesome! I economized much code!
Thanks for the sharing. @ small erroes maybe related the the version of the libraries your using.
if using BeanUtils of spring, the copy order is reversed. its
BeanUtils.copyProperties(json, entity)
findOne might be replaced with findById.
Example with reactive streams
...
import io.micrometer.core.annotation.Timed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.io.Serializable;
public abstract class GenericRestController<T, ID extends Serializable> {
private final Logger log = LoggerFactory.getLogger(VenueController.class);
protected ReactiveMongoRepository<T, ID> repo;
public GenericRestController(ReactiveMongoRepository<T, ID> repo) {
this.repo = repo;
}
@GetMapping
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public Flux<T> listAll() {
return repo.findAll();
}
@PostMapping
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public Mono<ResponseEntity<T>> create(@RequestBody T entry) {
return repo.save(entry)
.map(o -> new ResponseEntity<T>(o, HttpStatus.CREATED))
.onErrorMap(err -> {
log.error("Error occured while creating entity", err);
return new InternalServerErrorException("Error occured while creating entity", ErrorConstants.ERR_CREATION);
});
}
@GetMapping(value = "/{id}")
@Timed
@Secured(AuthoritiesConstants.USER)
public Mono<ResponseEntity<T>> get(@PathVariable ID id) {
return repo.findById(id)
.map(o -> new ResponseEntity<>(o, HttpStatus.OK))
.switchIfEmpty(Mono.error(new EntryNotFoundException("Entry not found with id " + id, "")))
.onErrorMap(err -> {
log.error("Error occured while fetching entity", err);
return new InternalServerErrorException("Error occured while fetching entity", ErrorConstants.ERR_FETCHING);
});
}
@PutMapping(value = "/{id}")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public Mono<ResponseEntity<T>> update(@PathVariable ID id, @RequestBody T json) {
return repo.findById(id)
.switchIfEmpty(Mono.error(new EntryNotFoundException("Entry not found with id " + id + ".No update was done.", "")))
.onErrorMap(err -> {
log.error("Error occured while fetching entity", err);
return new InternalServerErrorException("Error occured while fetching entity", ErrorConstants.ERR_FETCHING);
})
.doOnNext(entity -> BeanUtils.copyProperties(json, entity))
.flatMap(e -> repo.save(e))
.map(saved -> new ResponseEntity<T>(saved, HttpStatus.OK));
}
@DeleteMapping(value = "/{id}")
@Timed
@Secured(AuthoritiesConstants.ADMIN)
public Mono<ResponseEntity> delete(@PathVariable ID id) {
return repo.findById(id)
.switchIfEmpty(Mono.error(new EntryNotFoundException("Entry not found with id " + id, "")))
.flatMap(o -> repo.deleteById(id))
.map((r) -> new ResponseEntity(HttpStatus.NO_CONTENT));
}}
This only work if ID is String.
Spring can't deduce the type of @PathVariable ID id
Awesome !