Java Spring Boot Rest
RestController
Design Pattern - Service Layer Pattern
- Service Layer Pattern: Controller → Service → Repository → Database
--/your/app/
|-- configuration/
|-- SecurityConfiguration.java
|-- controller/
|-- EntitĂ Controller.java
|-- entity/
|-- EntitĂ .java
|-- repository/
|-- EntitĂ Repository.java
|-- service/
|-- EntitĂ Service.javaAlcune annotazioni da ricordare:
@RestController@GetMapping("/{name}/delete")- Name nel punto precedente si ottiene specificando nell’input del metodo
@PathVariable - Supponendo di avere
@GetMapping("/add"), che richiede l’attributo “name”, nell’input del metodo della classe andrò ad aggiungere:@RequestParam("name") String name @ExceptionHandler({ IndexOutOfBoundsException.class, EmptyResultDataAccessException.class })- Per gestire le eccezioni
Esempio completo di RestController
Fonte: đź”— Github - krsh - insecuresite
Say i have this entity made with Hibernate (JPA)
package xyz.krsh.insecuresite.rest.dao;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;
/*
This class represents a boardgame.
A board
- must have a name
- could have a price, a quantity and a description
*/
@Entity
@Table(name = "Boardgame")
public class Boardgame {
@Id
private String name = null;
@Column
private float price = 0.0f;
@Column
private int quantity = 0;
@Column
private String description = null;
@ManyToMany
private Set<Order> orders = new HashSet<>();
public Boardgame() { // Required by JPA
}
public Boardgame(String name) {
this.name = "";
if (name != null) {
this.name = name;
}
this.description = "";
}
public Boardgame(String name, float price, int quantity, String description) {
this.name = name;
this.price = price;
this.quantity = quantity;
this.description = description;
}
public String getId() {
return this.getName();
}
public String getName() {
return name;
}
public float getPrice() {
return price;
}
public int getQuantity() {
return quantity;
}
public String getDescription() {
return description;
}
public Set<Order> getOrders() {
return orders;
}
public void setPrice(float p) {
this.price = p;
}
public void setQuantity(int q) {
this.quantity = q;
}
public void setDescription(String desc) {
this.description = desc;
}
public String toString() {
return "Boardgame{" + "name(id)=" + name +
", price: " + price +
", quantity: " + quantity +
", description: " + description +
"} ";
}
}This is the repository
/*
* Implements the Repository pattern for Boardgame by extending the CrudRepository
*/
@Repository
public interface BoardgameRepository extends CrudRepository<Boardgame, String> {
List<Boardgame> findByNameContaining(String name);
List<Boardgame> findAll();
}This is the controller
@RestController
@RequestMapping("/api/boardgames")
public class BoardgameController {
@Autowired
BoardgameService boardgameService;
/*
* Returns every boardgame or the ones that match the query value
*/
@GetMapping
@ResponseBody
public List<Boardgame> find(@RequestParam(name = "q", defaultValue = "") String queryTerm)
throws ItemNotFoundException {
return boardgameService.findByNameContaining(queryTerm);
}
/*
* Get an existing boardgame querying using his name (primary key)
* Returns only the first result
*/
@GetMapping("/{name}")
@ResponseBody
Boardgame getById(@PathVariable String name) throws IndexOutOfBoundsException {
return boardgameService.getById(name);
}
/*
* Add a new boardgame to the database by REST call
* Request parameters are: name, price, quantity and description
*/
@GetMapping("/add")
@ResponseBody
public Boardgame addBoardgameReq(@RequestParam("name") String name,
@RequestParam("price") float price,
@RequestParam("quantity") int quantity,
@RequestParam("description") String description,
HttpServletRequest request) throws MissingServletRequestParameterException {
return boardgameService.addBoardgame(name, price, quantity, description);
}
/*
* Edit an existing boardgame by specifying his name and optional parameters
* that will replace the older ones
* Return the Boardgame with newest values
*/
@GetMapping(value = "/{name}/edit")
@ResponseBody
public Boardgame ediBoardgame(@PathVariable String name,
@RequestParam(value = "price", required = false) Float price,
@RequestParam(value = "quantity", required = false) Integer quantity,
@RequestParam(value = "description", required = false) String description,
HttpServletRequest request) throws ItemNotFoundException {
return boardgameService.editBoardgame(name, price, quantity, description, request);
}
/*
* Delete a Boardgame by specifing his name (id)
* Return a success message
*
*/
@GetMapping("/{name}/delete")
public String deleteBoardgame(@PathVariable String name) throws EmptyResultDataAccessException {
return boardgameService.deleteBoardgame(name);
}
/*
* Exception Handlers
*/
// Occurrs when you can't find any boardgames
@ExceptionHandler({ ItemNotFoundException.class, IndexOutOfBoundsException.class,
EmptyResultDataAccessException.class })
public ApiError handleIndexOutOfBoundsException() {
return new ApiError("Bordgame not found, retry", HttpStatus.NOT_FOUND);
}
@ExceptionHandler({ MissingServletRequestParameterException.class })
public ApiError handleMissingParametersException() {
return new ApiError("Bad Parameters: required name, price, quantity and description", HttpStatus.BAD_REQUEST);
}
}Logic is managed by a Service:
@Service
public class BoardgameService {
@Autowired
BoardgameRepository boardgameRepository;
@Autowired
OrderedBoardgamesRepository orderedBoardgameRepository;
public List<Boardgame> findByNameContaining(String queryTerm) throws ItemNotFoundException {
List<Boardgame> queryResult = boardgameRepository.findByNameContaining(queryTerm);
if (queryResult.isEmpty()) {
throw new ItemNotFoundException();
}
return queryResult;
}
public Boardgame getById(String name) {
Boardgame boardgame = boardgameRepository.findByNameContaining(name).get(0);
return boardgame;
}
public Boardgame addBoardgame(String name, float price, int quantity, String description) {
Boardgame newBoardgame = new Boardgame(name, price, quantity, description);
boardgameRepository.save(newBoardgame);
return newBoardgame;
}
public Boardgame editBoardgame(String name, Float price, Integer quantity, String description,
HttpServletRequest request)
throws ItemNotFoundException {
Boardgame boardgame;
List<Boardgame> queryResult = this.findByNameContaining(name);
if (queryResult.size() == 0 || queryResult.isEmpty() == true) {
throw new IndexOutOfBoundsException();
} else {
boardgame = queryResult.get(0);
}
// Check existance of params
boolean priceParamExists = request.getParameterMap().containsKey("price");
boolean quantityParamExists = request.getParameterMap().containsKey("quantity");
boolean descriptionParamExists = request.getParameterMap().containsKey("description");
// if price exists as parameter in the HTTP request, change the price
if (priceParamExists) {
boardgame.setPrice(price);
}
if (quantityParamExists) {
boardgame.setQuantity(quantity);
}
if (descriptionParamExists) {
boardgame.setDescription(description);
}
// update the boardgames with new values
boardgameRepository.update(boardgame);
return boardgame;
}
public String deleteBoardgame(String name) {
Optional<List<OrderedBoardgames>> obQueryResult = orderedBoardgameRepository.findByBoardgameName(name);
Optional<Boardgame> bQueryResult = boardgameRepository.findById(name);
if (obQueryResult.isPresent() && bQueryResult.isPresent()) {
List<OrderedBoardgames> list = obQueryResult.get();
for (OrderedBoardgames ob : list) {
orderedBoardgameRepository.delete(ob);
}
Boardgame boardgame = bQueryResult.get();
boardgameRepository.delete(boardgame);
}
return "Successfully deleted " + name + " ";
}
}Optional parameters
âť“ How to deal with optional request parameters in Spring Boot?
Consider this code snippet:
@RequestMapping("/searchBar")
public String searchBar(@RequestParam(name="q") String q){
return repository.findAll();
}There are three options:
- Uso di optional
public String searchBar(@RequestParam(name="q") Optional<String> q){
if (q.isPresent()){
} //OK
}- Uso di
@RequestParam(name="q", defaultValue="") - Uso di
@RequestParam(name="q", required= false)
Example code to avoid:
if (q == null){ return repository.findByNameContaining(q); }
Nested @Service
Say you have OneService and TwoService, OneService calls TwoService, both have different repository from different databases.
One hack i find that works is to add both in the controller with the @Autowired like:
@Controller
public class myController {
@Autowired
OneService oneService;
@Autowired
TwoService twoService;
//...
}