Commit cfe8a578 by Patryk Czarnik

Kontrolery RESTowe

parent 81c5a6a5
package sklep.rest;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import sklep.model.Customer;
import sklep.repository.CustomerRepository;
@RestController
@RequestMapping("/rest/customers")
public class CustomerEndpoint {
@Autowired
private CustomerRepository customerRepository;
@GetMapping
public List<Customer> listaKlientow(Model model) {
return customerRepository.findAll();
}
@GetMapping("/{email}")
public Customer jedenKlient(Model model, @PathVariable("email") String email) {
return customerRepository.findById(email).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
}
}
package sklep.rest;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ResponseStatusException;
import sklep.model.Order;
import sklep.repository.OrderRepository;
@RestController
@RequestMapping("/rest/orders")
public class OrderEndpoint {
@Autowired
private OrderRepository orderRepository;
@GetMapping
public List<Order> readAll() {
return orderRepository.findAll();
}
@GetMapping("/{id}")
public Order readOne(@PathVariable("id") int id) {
Optional<Order> order = orderRepository.findById(id);
if(order.isPresent()) {
return order.get();
} else {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Nie ma zamówienia o numerze " + id);
}
}
}
package sklep.rest;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;
import sklep.model.Product;
import sklep.photo.PhotoUtil;
import sklep.repository.ProductRepository;
import java.math.BigDecimal;
import java.util.List;
// @RestController odpowiada adnotacji @Controller z dodatkową adnotacją @ResponseBody
// Inaczej mówiąc, metody nie zwracają nazw szablonów, lecz dane, które mają być odesłane do klienta.
@RestController
@RequestMapping("/rest/products")
public class ProductEndpoint {
private ProductRepository productRepository;
private PhotoUtil photoUtil;
// Tutaj stosujemy wstrzykiwanie przez konstruktor.
public ProductEndpoint(ProductRepository productRepository, PhotoUtil photoUtil) {
this.productRepository = productRepository;
this.photoUtil = photoUtil;
}
@GetMapping(produces="application/json")
public List<Product> getProducts() {
return productRepository.findAll();
}
@GetMapping(path="/{id}", produces="application/json")
public Product getProduct(@PathVariable int id) {
return productRepository.findById(id).orElseThrow(
() -> new ResponseStatusException(HttpStatus.NOT_FOUND,
"Brak produktu o numerze " + id));
}
// Ta metoda pokazana tylko po to, aby wytłumaczyć, że struktura adresów powinna odpowiadać logicznej strukturze danych.
// W prawdziwej aplikacji raczej nie dochodzi się do poziomu pojedynczych pól. Teoretycznie można.
@GetMapping(path="/{id}/price", produces="application/json")
public BigDecimal getPrice(@PathVariable Integer id) {
return getProduct(id).getPrice();
}
/*
* Operacja PUT służy do zapisania danych POD PODANYM ADRESEM.
* Na przykład PUT products/2/price z wartością 100 powinno ustawić w produkcie nr 2 cenę 100.
* Jeśli PUT zadziała, to następnie GET wysłany pod ten sam adres powinien odczytać te same dane,
* które PUT zapisał (być może w innym formacie - to inny temat)
*
* PUT najczęściej jest używany do aktualizacji istniejących danych
* (pojedynczych wartości albo całych rekordów), ale może być też użyty do
* zapisania nowych danych. To, co najważniejsze, to fakt, że PUT zapisuje dane
* pod konkretnym adresem, do którego jest wysyłany.
*
* Aby odczytać dane, które przysłał klient, metoda ma jeden parametr oznaczony @RequestBody.
* To do tego parametru Spring przekaże dane odczytane z "body" zapytania.
*/
@PutMapping("/{id}/price")
public void setPrice(@PathVariable Integer id, @RequestBody BigDecimal newPrice) {
Product product = productRepository.findById(id).orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
product.setPrice(newPrice);
productRepository.save(product);
}
/*
* PUT może również służyć do zapisania całego rekordu, ale zwn, że musi być
* skierowany pod ten adres, pod którym rekord zostanie faktycznie zapisany, w
* praktyce PUT jest uzywany do aktualizacji rekordów (UPDATE).
*
* Aby w aplikacji Springowej, w której jest włączone security, działały zapytania POST i PUT,
* trzeba wyłączyć zabezpieczenie "CSRF":
* .and().csrf().disable()
*/
@PutMapping("/{id}")
public void update(@PathVariable("id") Integer productId, @RequestBody Product product) {
// Aby mieć pewność, że zapisujemu produkt o typ ID, wpisuję productId z URL-a.
// Ewentualnie możnaby jeszcze sprawdzić czy rekord istnieje, czy ID się zgadza
// i jeśli coś jest nie tak, to wyrzucić wyjątek.
product.setId(productId);
productRepository.save(product);
}
/*
* POST jest najbardziej ogólną metodą HTTP; oznacza, że klient
* "wysyła jakieś dane na serwer", a serwer odsyła jakąś odpowiedź.
* W zasadzie POST może służyć do wszystkiego.
*
* W praktyce POST bardzo często służy do dodawania nowych rekordów, ponieważ
* gdy tworzymy nowy rekord, to nie znamy z góry jego ID i nie wiemy pod jakim
* URL-em zostanie zapisany (nie da się więc użyć PUT). Stąd wzięła REST-owa
* konwencja, że aby dodać nowy rekord do katalogu, wysyła się POST z danymi
* tego rekordu pod ogólny adres całego katalogu.
*/
@PostMapping
public Product insert(@RequestBody Product product) {
// Aby mieć pewność, że zapytanie tworzy nowy rekord, ustawiam productId na null.
product.setId(null);
productRepository.save(product);
/* Operacja save (a wewnętrznie persist z Hibernate) spowoduje ustawienie nowego ID w obiekcie.
* Warto taką informację przekazać klientowi. Można:
* 1) odesłać uzupełniony rekord (i tak zrobimy tutaj),
* 2) odesłać "małego JSON-a" z informacją o tym ID
* (i innymi informacjami, które serwer chce przekazać klientowi)
* 3) tylko nagłówek Location z URL-em nowego rekordu (to zobaczymy w wersji JAX-RS).
*/
return product;
}
@DeleteMapping("/{id}")
public void delete(@PathVariable("id") Integer productId) {
try {
productRepository.deleteById(productId);
} catch (EmptyResultDataAccessException e) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Brak produktu nr " + productId);
}
}
/* Większość komunikacji w usługach REST odbywa się w formacie JSON,
* ale czasami używany jest też format XML,
* a dla niektórych danych stosujemy bezpośrednio jakiś format specjalny, np. PNG, JPG dla obrazów, PDF dla wydruków itp.
*/
@GetMapping(path="/{id}/photo", produces="image/jpeg")
public byte[] getPhoto(@PathVariable("id") int productId) {
return photoUtil.readBytes(productId);
}
@PutMapping(path="/{id}/photo", consumes="image/jpeg")
public void uploadPhoto(@PathVariable("id") int productId, @RequestBody byte[] bytes) {
photoUtil.writeBytes(productId, bytes);
}
}
...@@ -36,6 +36,8 @@ ...@@ -36,6 +36,8 @@
<li><a href="/products/new">nowy produkt</a></li> <li><a href="/products/new">nowy produkt</a></li>
<li><a href="/products/1/edit">edycja produktu</a></li> <li><a href="/products/1/edit">edycja produktu</a></li>
<li><a href="/products/2/photo">przykładowe zdjęcie</a></li> <li><a href="/products/2/photo">przykładowe zdjęcie</a></li>
<li><a href="/products/max">najdroższy produkt</a></li>
<li><a href="/products/2/ile_drozszych">ile droższych</a> niż odkurzacz</li>
</ul> </ul>
<h2>Klienci</h2> <h2>Klienci</h2>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment