Mapowanie danych w requestach HTTP

W rozdziale Spring Framework - Spring MVC wspominamy, że po otrzymaniu żądania HTTP framework MVC musi wiedzieć, w jaki sposób takie żądanie ma zostać obsłużone. Po dopasowaniu ścieżki i wybraniu odpowiedniej metody Java (zgodnie z opisem Spring MVC: Mapowanie requestów HTTP), Spring przestępuje do analizy adnotacji umieszczonych w obrębie tej metody.

I tu od razu uwaga. Pełna specyfikacja Springa MVC załączona w postaci linka na dole strony, zawiera większą ilość adnotacji, niż te opisywane przez nas. Wynika to z tego, że nie chcemy robić przedruku z oficjalnej dokumentacji, lecz wolimy skupić się na tym co jest najczęściej używane i te elementy opisać nieco dokładniej wraz z przykładami. Oczywiście zachęcamy również do odwiedzania zewnętrznych linków, gdyż będą one dobrym uzupełnieniem przygotowanego przez nas materiału.

Wracając do tematu adnotacji należy przypomnieć, że dzielimy je na dwie kategorie: argumenty metod oraz zwracane wartości metod. Przedstawiona poniżej lista zawiera w większości opis adnotacji dotyczących argumentów metod. Ostatnia adnotacja @ResponseBody dotyczy zwracanej wartości:
  • Adnotacja @RequestParam

    Adnotacja jest stosowana w przypadku, gdy wysyłamy żądanie ustawiając w ścieżce nazwę parametru z wartością, np.:
    HTTP GET: http://.../users?id=23

    Wówczas metodę oznaczamy:
    @GetMapping(value = "/users")
    public ResponseEntity<UserResponseDTO> getUser(@RequestParam("id") Long id)
                                                                       throws Exception {
    
        ...
        
        return new ResponseEntity<>(appaUserResponseDTO, HttpStatus.OK);
    }
    
    Możemy określić czy parametr zawsze musi być ustawiony. Domyślnie required jest ustawione na true. W przykładzie wyżej nie podaliśmy w ogóle tego atrybutu (required), więc przyjmuje on wartość domyślną. W przypadku gdybyśmy chcieli ustawić wartość false zapis wyglądałby w ten sposób:

    @RequestParam(value="id", required="false") Long id
  • Adnotacja @PathVariable

    Adnotacja jest stosowana w przypadku, gdy wysyłamy żądanie ustawiając wartość parametru bezpośrednio w ścieżce, np.:
    HTTP GET: http://.../appaitems/11

    Wówczas metodę oznaczamy:
    @GetMapping("/{id}")
    public ResponseEntity<ItemResponseDTO> getAppaItem(@PathVariable Long id)
                                                                    throws Exception {
    
    
        ...
    
        return new ResponseEntity<>(appaItemResponseDTO, HttpStatus.OK);
    }
    
    Od Springa w wersji 5 możemy wreszcie określać czy wartosć parametru zawsze musi być ustawiona. Teoretycznie wersja 4.3 umożliwiała to poprzez użycie Optional z Javy 8, niemniej obecnie mamy ten atrybut wbudowany już bezpośrednio w adnotację. Domyślnie required jest ustawione na true. W przykładzie wyżej nie podaliśmy w ogóle tego atrybutu (required), więc przyjmuje on wartość domyślną. W przypadku gdybyśmy chcieli ustawić wartość false zapis wyglądałby w ten sposób:

    @PathVariable(value="id", required="false") Long id
  • Adnotacja @RequestBody

    Adnotacja jest stosowana w przypadku, gdy przesyłamy dane w body żądania. Wtedy tworzymy obiekt, którego pola muszą zgadzać się z polami w wysyłanym obiekcie. Zgodnie z obowiązującymi trendami w większości przypadków będzie to obiekt JSON.

    Wysyłając żądanie na ścieżkę:
    HTTP POST: https://.../password/token

    wysyłamy tak naprawdę JSON-a (jako ciało wiadomości):
    {
        "email":"jan.kowalski@javappa.admin.com"
    }                        
    
    W tym momencie Spring wykonuje konwersję do obiektu Java (adnotacja @NotNull nie ma tu związku - uczestniczy w walidacji pola):
    public class TokenRequestDTO {
    
        @NotNull
        private String email;
        
        public TokenRequestDTO() {    	
        }
        
        public String getEmail() {
        	return email;
        }
    }
    
    Ostatecznie w metodzie kontrolera otrzymamy obiekt TokenRequestDTO wraz z wypełnionym polem email:
    @PostMapping("/password/token")
    public ResponseEntity<EmailResponseDTO> sendEmailWithToken(
                        @Valid @RequestBody TokenRequestDTO toke...) throws Exception {
    
        ...
    
        return new ResponseEntity<>(emailResponseDTO, HttpStatus.OK);
    }
    
    W kontekście omawianych operacji adnotacja @Valid nie ma związku - jest dodana jedynie w celu sprawdzenia poprawności pola (email nie może być pusty).
  • Adnotacja @RequestPart

    Adnotacja również jest stosowana w przypadku, gdy przesyłamy dane w body żądania. Natomiast różnica polega na tym, że używamy jej w kontekście żądań określonych jako multipart/form-data. Powoduje to, że jest użyteczna w przypadku przesyłania na serwer plików, np. zdjęć.

    Dodatkowo jeśli razem ze zdjęciem chcemy przesłać obiekt, który jest JSON-em (np. z metadanymi zdjęcia), wówczas jego też określamy adnotacją @RequestPart i podobnie jak w przypadku @RequestBody tworzymy obiekt, którego pola muszą zgadzać się z polami w wysyłanym obiekcie JSON.
        @PostMapping
        public ResponseEntity<ItemIdResponseDTO> saveOrUpdateAppaItem(
        		@Valid @RequestPart ItemRequestDTO appaItemRequestDTO,
        		@RequestPart(required = false) @Mult... MultipartFile file )
                                                                    throws Exception {
        
            ...
    
        	return new ResponseEntity<ItemIdResponseDTO>(
                            new ItemIdResponseDTO(appaItemId), HttpStatus.OK);
        }
    
    Dla tej adnotacji również możemy określić czy parametr zawsze musi być ustawiony. Domyślnie required jest ustawione na true. Jak widać my umożliwiamy w tym przykładzie wysłanie żądania również bez załączonego zdjęcia.
  • Adnotacja @ResponseBody

    Adnotacja jest stosowana w przypadku, gdy chcemy zwrócić serializowane dane (obiekty sprowadzone do postaci binarnej) z metody kontrolera (choć nie tylko kontrolera, ale o tym za chwilę). Natomiast jeśli wszystkie nasze metody w danym kontrolerze mają zwracać tylko i wyłącznie dane serializowane, wówczas możemy skorzystać z adnotacji @RestController. Ta meta-adnotacja łączy w sobie adnotacje @Controller oraz @ResponseBody.
    @RestController
    @RequestMapping("/api/appaform/appacategories")
    public class CategoryController {
    
        ...
    
    }
    
    Podobnie sprawa wygląda z klasami oznaczonymi adnotacją @ControllerAdvice. W tym przypadku również jeśli w klasie wszystkie metody mają zwracać dane serializowane, to możemy użyć meta-adnotacji łączącej adnotacje @ControllerAdvice oraz @ResponseBody. Użyjemy wtedy adnotacji o nazwie @RestControllerAdvice.

    W dalszym ciągu możemy jednak używać adnotacji @ResponseBody w czystej postaci, jeśli nie mamy takiego komfortu, że wszystkie metody mogą być nią opatrzone automatycznie. Wtedy użyjemy jej tylko tam gdzie tego potrzebujemy.
    @ExceptionHandler(MultipartException.class)
    @ResponseBody
    public ErrorMessageResponseDTO handleFileLimit(Exception exception) {
    
        LOG.error(exception.getMessage(), exception);
        ErrorMessageResponseDTO errorMessageResponseDTO = new ErrorMessageResponseDTO();
        errorMessageResponseDTO.setMessage(exception.getLocalizedMessage());
        
        return errorMessageResponseDTO;
    }
    
    W powyższym przykładzie używamy tej adnotacji w klasie porady dla kontrolerów (@ControllerAdvice), w której obsługujemy różne rodzaje błędów w aplikacji.
Rekomendacja
W zasadzie to od czego należałoby zacząć, to od zwrócenia uwagi, że umiejętność użycia wszystkich wyżej wymienionych adnotacji jest absolutnym must-have w trakcie pracy ze Springiem, szczególnie w kontekście programowania aplikacji webowych opartych o styl REST. Należy zatem bardzo dobrze to opanować.

Ciekawostką jest fakt, że ze wszystkich tych adnotacji najrzadziej spotykamy w projektach pierwszą z nich, a więc @RequestParam. Wynika to pewnie z tego, że albo w systemach potrzebujemy przesyłać duże ilości parametrów naraz (np. dane formularza) i robimy to w body wiadomości, albo też jeśli już wymagane jest użycie pojedyńczych parametrów (np. przy pobieraniu obiektu po id), to zgodnie z REST wykonujemy to poprzez stosowanie parametrów ścieżki @PathVariable.
Praktyka


Przykłady pochodzą z naszej aplikacji oraz Kursu Aplikacji Web - Mega pakiet,
Zdjęcie autora
Autor: Jarek Klimas
Data: 03 stycznia 2024
Labele: Backend, Podstawowy, Java
Topowe Materiały
Spring IO: Mapping Requests
Baeldung: Spring Web Annotations
Baeldung: Guide to Spring Handler Mappings
Baeldung: Spring RequestMapping

Udemy: [NEW] Spring Boot 3, Spring 6 & Hibernate for Beginners  —  polskie napisy

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .


Pozycjonowanie stron: Grupa TENSE