Методи обробника з анотацією @RequestMapping
мають гнучку сигнатуру і можуть обирати з низки
підтримуваних аргументів і значень методу контролера, що повертаються.
Аргументи методу
У наступній таблиці описані підтримувані аргументи методу контролера. Реактивні типи не підтримуються для жодних аргументів.
Аргумент java.util.Optional
з JDK 8 підтримується як аргумент методу в поєднанні з анотаціями, що мають
атрибут required
(наприклад, @RequestParam
, @RequestHeader
та інші), і
еквівалентний required=false
.
Аргумент методу контролера | Опис |
---|---|
|
Забезпечує типізований доступ до параметрів запиту та атрибутів запиту та сесії без прямого використання Servlet API. |
|
Призначений для вибору будь-якого конкретного типу запиту або відповіді — наприклад,
|
|
Забезпечує наявність сесії. Як наслідок, такий аргумент ніколи не |
|
API проштовхувального засобу складання Servlet 4.0 для програмного проштовхування (передачі ) ресурсів
HTTP/2. Зверни увагу, що, згідно специфікації Servlet, впроваджуваний екземпляр |
|
Поточний автентифікований користувач — можливо, конкретний клас реалізації Зверни увагу, що цей аргумент не розпізнається прискорено, якщо має анотацію, призначену для того, щоб
дозволити кастомному розпізнавачу розпізнати його, перш ніж повернутися до розпізнавання за
замовчуванням
через |
|
HTTP-метод запиту. |
|
Поточні регіональні налаштування запиту, що визначаються найбільш конкретним доступним |
|
Часовий пояс, пов'язаний з поточним запитом, визначений |
|
Призначений для забезпечення доступу до сирого тіла запиту в тому вигляді, в якому воно представлене в Servlet API. |
|
Призначений для забезпечення доступу до сирого тіла відповіді, відкритого Servlet API. |
|
Призначений для забезпечення доступу до змінних шаблонів URI-ідентифікаторів. |
|
Призначений для забезпечення доступу до пар "ім'я-значення" в сегментах шляху URI-ідентифікаторів. |
|
Призначений для доступу до параметрів запиту сервлета, включно з багатокомпонентними файлами. Значення параметрів перетворюються на оголошений тип аргументів методу. Зверни увагу, що використання анотації |
|
Призначений для забезпечення доступу до заголовків запитів. Значення заголовків перетворюються на оголошений тип аргументу методу. |
|
Призначений для забезпечення доступу до cookie. Значення cookie перетворюються на оголошений тип аргументу методу. |
|
Для доступу до HTTP-запиту. Вміст тіла перетворюється на оголошений тип аргументу методу за допомогою
реалізацій |
|
Призначений для забезпечення доступу до заголовків та тіла запиту. Тіло перетворюється за допомогою
|
|
Призначений для забезпечення доступу до компоненту в запиті |
|
Призначені для забезпечення доступу до моделі, яка використовується в HTML-контролерах і відображається в шаблонах як частина візуалізації представлення. |
|
Встановлює атрибути, які будуть використовуватися у разі перенаправлення (тобто будуть додані до рядку запиту), flash атрибути, які тимчасово зберігатимуться до надходження запиту після перенаправлення. |
|
Для доступу до існуючого атрибуту в моделі (екземпляр якого створюється, якщо він відсутній) з прив'язкою даних та валідацією. Зверни увагу, що використовувати анотацію |
|
Призначений для забезпечення доступу до помилок валідації та прив'язки даних для об'єкта команди (тобто
аргументу з анотацією |
|
Призначений для позначення завершення обробки форми, що викликає очищення атрибутів сесії, оголошених
через анотацію |
|
Призначений для підготовки URL-адреси, пов'язаної з хостом, портом, схемою, контекстним шляхом поточного запиту та літеральним компонентом відображення сервлету. |
|
Призначений для забезпечення доступу до будь-якого атрибуту сесії, на відміну від атрибутів моделі, що
зберігаються в сесії в результаті оголошення анотації |
|
Призначений для забезпечення доступу до атрибутів запиту. |
Будь-який інший аргумент |
Якщо аргумент метод не відповідає жодному з попередніх значень у цій таблиці і є простим типом (як
визначено BeanUtils#isSimpleProperty), він дозволяється як |
Значення, що повертаються
У наступному таблиці описані підтримувані значення методу контролера. Реактивні типи підтримуються для всіх значень, що повертаються.
Повертане значення методу контролера | Опис |
---|---|
|
Значення, що повертається, перетворюється через реалізацію |
|
Значення, що повертається, яке зазначає повну відповідь (включно з HTTP-заголовками і тілом),
перетворюється через
реалізацію |
|
Призначено для повернення відповіді із заголовками і без тіла. |
|
Ім'я подання, яке має бути розпізнане за допомогою реалізацій |
|
Екземпляр |
|
Атрибути, що додаються до неявної моделі, з ім'ям подання, що неявно визначається через |
|
Атрибут, доданий до моделі, ім'я подання якого неявно визначається через Зверни увагу, що анотація |
|
Атрибути подання та моделі, що використовуються, і, за необхідності, статус відповіді. |
|
Вважається, що метод з типом Якщо жодне з вищеперелічених значень не вірне, повертається тип |
|
Здійснює будь-яке з попередніх значень, що повертаються, асинхронно з будь-якого потоку — наприклад, в результаті будь-якої події або зворотного виклику. |
|
Перетворює будь-яке з перерахованих вище значень, що повертаютья асинхронно в потоці, керованому Spring MVC. |
|
Найбільш зручна альтернатива |
|
Асинхронно породжує потік об'єктів для запису у відповідь за допомогою реалізацій |
|
Асинхронний запис в |
Реактивні типи — Reactor, RxJava, або інші через |
Альтернатива У потокових сценаріях (наприклад, |
Будь-яке інше значення, що повертається |
Будь-яке значення, що повертається, яке не відповідає жодному з попередніх значень у цій таблиці і є
|
Перетворення типу
Деякі анотовані аргументи методу контролера, які представляють введення запиту на
основі String
(такі як @RequestParam
, @RequestHeader
,
@PathVariable
, @MatrixVariable
і @CookieValue
), можуть вимагати перетворення
типу, якщо аргумент оголошений не як String
.
У таких випадках перетворення типу застосовується
автоматично на основі конфігурованих перетворювачів. За замовчуванням підтримуються прості типи (int
,
long
, Date
та інші). Ти можеш налаштувати перетворення типів через
WebDataBinder
або через реєстрацію Formatters
у FormattingConversionService
.
Практичним питанням при перетворенні типів є обробка порожнього вихідного значення рядка. Таке значення
розглядається як відсутнє, якщо воно стає null
у результаті перетворення типу. Це може бути характерним
для Long
, UUID
та інших цільових типів. Якщо тобі потрібно допустити впровадження null
,
або використовуй прапор required
в анотації аргументу, або оголоси аргумент як
@Nullable
.
Починаючи з версії 5.3, не-null аргументи будуть виконуватися навіть
після перетворення типів. Якщо передбачається, що метод обробника буде приймати і порожнє значення, оголоси
аргумент як @Nullable
, або познач його як required=false
у відповідній анотації @RequestParam
тощо. Це найкращий спосіб і рекомендоване рішення для регресій, що виникають при оновленні до версії
5.3. @PathVariable
є обов'язковим. Null-значення після перетворення буде оброблено як порожнє вихідне
значення, тому буде згенеровано відповідні варіанти винятку Missing…Exception
.
Матричні змінні
У RFC 3986
описані пари "ім'я-значення" в сегментах шляху. У Spring MVC ми називаємо їх "матричними змінними", ґрунтуючись на
"старому пості" Тіма Бернерса-Лі, але
їх також можна назвати параметрами шляху URI-ідентифікатора. Матричні змінні можуть з'являтися в будь-якому сегменті
шляху, водночас кожна змінна відділяється точкою з комою, а кілька значень – комою (наприклад, /cars;color=red,green;year=2012
).
Множинні
значення також можна вказати через імена змінних, що повторюються (наприклад,
color=red;color=green;color=blue
).
Якщо очікується, що URL-адреса міститиме матричні змінні, то відображення запиту для методу контролера повинно використовувати змінну URI-ідентифікатора для маскування вмісту такої матричної змінної та забезпечення успішного зіставлення запиту незалежно від порядку та наявності матричних змінних. У цьому прикладі використовується матрична змінна:
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {
// petId == 42
// q == 11
}<
// GET /pets/42;q=11;r=22
@GetMapping("/pets/{petId}")
fun findPet(@PathVariable petId: String, @MatrixVariable q: Int) {
// petId == 42
// q == 11
}
Враховуючи, що всі сегменти шляху можуть містити матричні змінні, іноді потрібно визначити, в якому змінному шляху повинна бути матрична змінна. У цьому прикладі показано, як це зробити:
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable(name="q", pathVar="ownerId") int q1,
@MatrixVariable(name="q", pathVar="petId") int q2) {
// q1 == 11
// q2 == 22
}
// GET /owners/42;q=11/pets/21;q=22
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable(name = "q", pathVar = "ownerId") q1: Int,
@MatrixVariable(name = "q", pathVar = "petId") q2: Int) {
// q1 == 11
// q2 == 22
}
Матрична змінна може бути визначена як необов'язкова і мати значення за замовчуванням, як це показано в наступному прикладі:
// GET /pets/42
@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {
// q == 1
}
// GET /pets/42
@GetMapping("/pets/{petId}")
fun findPet(@MatrixVariable(required = false, defaultValue = "1") q: Int) {
// q == 1
}
Щоб отримати всі матричні змінні, можна використовувати MultiValueMap
, як показано в наступному
прикладі:
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
@MatrixVariable MultiValueMap<String, String> matrixVars,
@MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
// GET /owners/42;q=11;r=12/pets/21;q=22;s=23
@GetMapping("/owners/{ownerId}/pets/{petId}")
fun findPet(
@MatrixVariable matrixVars: MultiValueMap<String, String>,
@MatrixVariable(pathVar="petId") petMatrixVars: MultiValueMap<String, String>) {
// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
// petMatrixVars: ["q" : 22, "s" : 23]
}
Зверни увагу, що необхідно активувати використання матричних змінних. У конфігурації MVC Java необхідно
встановити UrlPathHelper
при removeSemicolonContent=false
через Path Matching. У просторі
імен MVC на XML можна встановити <mvc:annotation-driven enable-matrix-variables="true"/>
.
@RequestParam
Анотацію @RequestParam
можна використовувати для прив'язки
параметрів запиту сервлета (тобто параметрів запиту або даних форми) до аргументу методу в контролері.
У наступному прикладі показано, як це зробити:
@Controller
@RequestMapping("/pets")
public class EditPetForm {
// ...
@GetMapping
public String setupForm( @RequestParam("petId") int petId, Model model) {
Pet pet = this.clinic.loadPet(petId);
model.addAttribute("pet", pet);
return "petForm";
}
// ...
}
- Використання анотації
@RequestParam
для прив'язкиpetId
.
import org.springframework.ui.set
@Controller
@RequestMapping("/pets" )
class EditPetForm {
// ...
@GetMapping
fun setupForm(@RequestParam("petId") petId: Int, model: Model): String {
val pet = this.clinic.loadPet(petId);
model["pet"] = pet
return "petForm"
}
// ...
- Використання анотації
@RequestParam
для прив'язкиpetId
.
За замовчуванням параметри методу, що використовують цю анотацію, є обов'язковими, але ти можеш вказати, що
параметр методу є необов'язковим, встановивши прапор required
анотації @RequestParam
у
false
або якщо оголосиш аргумент за допомогою функції-обгортки java.util. Optional
.
Перетворення типу застосовується автоматично, якщо тип параметра цільового методу не String
.
Оголошення типу аргументу як масиву або списку дозволяє кілька значень параметрів для одного і того ж імені параметра.
Якщо анотація @RequestParam
оголошена як Map<String, String>
або MultiValueMap<String, String>
без зазначення імені параметра в анотації, то Map заповнюється
значеннями параметрів запиту для кожного заданого імені параметра.
Зверни увагу, що використання анотації
@RequestParam
є необов'язковим (наприклад, для встановлення атрибутів). За замовчуванням будь-який
аргумент, який є простим типом значення (як визначено
BeanUtils#isSimpleProperty)
і не дозволений жодним іншим розпізнавачем аргументів, обробляється так, якби він був анотований
@RequestParam
.
Розглянемо наступний запит із заголовками:
Host localhost:8080 Accept text/html,application/xhtml+xml,application/xml;q=0.9 Accept-Language fr,en-gb;q=0.7,en;q=0.3 Accept-Encoding gzip,deflate Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive 300
У наступному прикладі показано, як отримати значення заголовків Accept-Encoding
та Keep-Alive
:
@GetMapping("/demo")
public void handle(
@RequestHeader(" Accept-Encoding") String encoding,
@RequestHeader("Keep-Alive") long keepAlive) {
//...
}
- Отримуємо значення заголовка
Accept-Encoding
. - Отримуємо значення заголовка
Keep-Alive
.
@GetMapping("/demo")
fun handle(
@RequestHeader("Accept-Encoding") encoding: String,
@RequestHeader("Keep-Alive") keepAlive: Long) {
//...
}
- Отримуємо значення заголовка
Accept-Encoding
. - Отримуємо значення заголовка
Keep-Alive
.
Якщо тип параметра цільового методу не є String
, перетворення типів застосовується автоматично.
Якщо анотація @RequestHeader
використовується в аргументі Map<String, String>
,
MultiValueMap<String, String>
, або HttpHeaders
, Map заповнюється всіма значеннями
заголовків. Наприклад, параметр методу, анотований @RequestHeader("Accept")
, може мати тип
String
,
а також String[]
або List<String>
.
@CookieValue
Анотацію @CookieValue
можна використовувати для прив'язки значень HTTP-даних типу cookie до аргументу
методу в контролері.
Розглянемо запит з наступним cookie:
JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84
У наступному прикладі показано, як отримати значення cookie:
@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {
//. ..
}
- Отримання значення cookie
JSESSIONID
.
@GetMapping("/demo")
fun handle(@CookieValue("JSESSIONID") cookie: String) {
//...
}
- Отримання значення cookie
JSESSIONID
.
Якщо тип параметра цільового методу не String
, перетворення типів застосовується автоматично.
@ModelAttribute
Анотацію @ModelAttribute
можна використовувати для аргументу
методу, щоб отримати доступ до атрибуту з моделі або створити його екземпляр, якщо він відсутній. На атрибут моделі
також накладаються значення параметрів запиту сервлета за протоколом HTTP, імена яких збігаються з іменами полів. Це
називається прив'язуванням даних, і воно позбавляє необхідності парсити і перетворювати окремі параметри запиту та
поля форм. У цьому прикладі показано, як це зробити:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {
// method logic...
}
@PostMapping( "/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@ModelAttribute pet: Pet): String {
// method logic...
}
Наведений вище екземпляр Pet
отриманий одним із наступних способів:
Витягується з моделі, куди його могли додати методом з анотацією @ModelAttribute .
Витягується з HTTP-сесії, якщо атрибут моделі був вказаний в анотації
@SessionAttributes
на рівні класу.Отриманий через якийсь
Converter
, де ім'я атрибуту моделі збігається з ім'ям значення запиту, наприклад змінної шляху або параметром запиту (див. наступний приклад).Створюється за допомогою конструктора за замовчуванням.
Створюється через "первинний конструктор" з аргументами, що відповідають параметрам запиту сервлета. Імена аргументів визначаються через анотацію
@ConstructorProperties
класів JavaBeans або через імена параметрів, що зберігаються під час виконання, в байт-коді.
Альтернативою використанню методу з анотацією @ModelAttribute для його надання або звернення до фреймворку для
створення атрибуту моделі є використання Converter<String, T>
для надання екземпляра. Це
застосовується, якщо ім'я атрибута моделі збігається з ім'ям значення запиту, такого як змінна шляху або
параметр запиту, і існує Converter
з String
у типу атрибута моделі. У наступному
прикладі ім'я атрибуту моделі — account
, що відповідає змінній шляху URI-ідентифікатора
account
,
а також існує зареєстрований Converter<String, Account>
, який може завантажити
Account
зі сховища даних:
@PutMapping("/accounts/{account}")
public String save( @ModelAttribute("account") Account account) {
// ...
}
@PutMapping ("/accounts/{account}")
fun save(@ModelAttribute("account") account: Account): String {
// ...
}
Після отримання екземпляра атрибуту моделі застосовується прив'язка даних. Клас WebDataBinder
зіставляє імена параметрів запиту сервлета (параметри запиту та поля форми) з іменами полів цільового об'єкта.
Узгоджені поля заповнюються після застосування перетворення типу, де це необхідно.
Прив'язка даних може
призвести до помилок. За замовчуванням виникає виняток BindException
. Однак для перевірки таких помилок
у методі контролера можна додати аргумент BindingResult
безпосередньо поруч із анотацією @ModelAttribute
,
як показано в наступному прикладі:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
- Додавання
BindingResult
поряд з анотацією@ModelAttribute
.
@PostMapping("/owners/{ownerId}/pets/{petId}/ edit")
fun processSubmit(@ModelAttribute("pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors ()) {
return "petForm"
}
// ...
}
- Додавання
BindingResult
поруч із анотацією@ModelAttribute
.
У деяких випадках може знадобитися доступ до атрибуту моделі без прив'язки до даних. Для таких випадків можна
впровадити Model
у контролер і звертатися до неї безпосередньо або, як варіант, встановити @ModelAttribute(binding=false)
,
як показано в наступному прикладі:
@ModelAttribute
public AccountForm setUpForm() {
return new AccountForm();
}
@ModelAttribute
public Account findAccount(@PathVariable String accountId) {
return accountRepository.findOne(accountId);
}
@PostMapping("update")
public String update(@Valid AccountForm form, BindingResult result,
@ModelAttribute(binding=false) Account account) {
// ...
}
- Налаштування
@ModelAttribute(binding=false)
.
@ModelAttribute
fun setUpForm(): AccountForm {
return AccountForm()
}
@ModelAttribute
fun findAccount(@PathVariable accountId : String): Account {
return accountRepository.findOne(accountId)
}
@PostMapping("update")
fun update(@Valid form: AccountForm, result: BindingResult,
@ModelAttribute(binding = false) account: Account): String {
// ...
}
- Налаштування
@ModelAttribute(binding=false)
.
Ти можеш автоматично застосовувати валідацію після прив'язки даних, додавши анотацію
javax.validation.Valid
або анотацію @Validated
зі Spring. У цьому прикладі показано, як це
зробити:
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@Valid @ModelAttribute("pet") Pet pet, BindingResult result) {
if (result.hasErrors()) {
return "petForm";
}
// ...
}
- Валідуємо екземпляр
Pet
.
@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
fun processSubmit(@Valid @ModelAttribute( "pet") pet: Pet, result: BindingResult): String {
if (result.hasErrors()) {
return "petForm"
}
// ...
}
Зверни увагу, що використання анотації @ModelAttribute
є необов'язковим (наприклад, для
встановлення атрибутів). За замовчуванням будь-який аргумент, який не є простим типом значення (як визначено BeanUtils#isSimpleProperty) і не дозволяється жодним іншим розпізнавальником
аргументів, обробляється так, якби він був анотований @ModelAttribute
. Це анотація лише на рівні
типу, яка оголошує атрибути сесії, використовувані конкретним контролером. Тут зазвичай перераховуються
імена атрибутів моделі або типи атрибутів моделі, які повинні прозорим чином зберігатися в сесії для
наступних запитів на доступ.
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
}
- Використання анотації
@SessionAttributes
.
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
}
- Використання анотації
@SessionAttributes
.
При першому запиті, коли атрибут моделі з ім'ям pet
додається до моделі, він автоматично
просувається та зберігається в HTTP-сесії сервлета. Він залишається там, поки інший метод контролера не
використовує аргумент методу SessionStatus
для очищення сховища, як показано в наступному
прикладі:
@Controller
@SessionAttributes("pet")
public class EditPetForm {
// ...
@PostMapping("/pets/{id }")
public String handle(Pet pet, BindingResult errors, SessionStatus status) {
if (errors.hasErrors) {
// ...
}
status.setComplete();
// ...
}
}
- Зберігання значення
Pet
у сесії сервлету. - Очищення значення
Pet
із сесії сервлета.
@Controller
@SessionAttributes("pet")
class EditPetForm {
// ...
@PostMapping("/pets/{id}")
fun handle(pet: Pet, errors: BindingResult, status: SessionStatus): String {
if (errors.hasErrors()) {
// ...
}
status.setComplete()
// ...
}
}
- Зберігання значення
Pet
у сесії сервлету. - Очищення значення
Pet
із сесії сервлета.
@SessionAttribute
Якщо потрібен доступ до заздалегідь існуючих атрибутів сесії, які управляються глобально (тобто поза контролером:
наприклад, фільтром) і можуть бути присутніми або відсутніми, можна використовувати анотацію
@SessionAttribute
для параметра методу, як показано в наступному прикладі:
@RequestMapping("/")
public String handle(@SessionAttribute User user) {
// ...
}
- Використання анотації
@SessionAttribute
.
@RequestMapping("/")
fun handle(@SessionAttribute user: User): String {
// ..
}
Для випадків використання, що вимагають додавання або видалення атрибутів сесії, розглянь можливість
впровадження org.springframework.web.context.request.WebRequest
або javax.servlet.http.HttpSession
метод контролера.
Для тимчасового зберігання атрибутів моделі в сесії як частини робочого процесу контролера, розглянь можливість
використання анотації @SessionAttributes
.
@RequestAttribute
Подобно до анотації @SessionAttribute
, можна використовувати анотації @RequestAttribute
для
забезпечення доступу до вже існуючих атрибутів запиту, створених раніше (наприклад,
Filter
або HandlerInterceptor
сервлетів):
@GetMapping("/")
public String handle(@RequestAttribute Client client) {
// ...
}
- Використання анотації
@RequestAttribute
.
@GetMapping("/")
fun handle(@RequestAttribute client: Client): String {
// ...
}
- Використання анотації
@ RequestAttribute
.
Атрибути перенаправлення
За замовчуванням всі атрибути моделі вважаються представленими у формі шаблону URI-ідентифікаторів в URL-адресі перенаправлення. З атрибутів, що залишилися, ті, які є примітивними типами або колекціями чи масивами примітивних типів, автоматично додаються як параметри запиту.
Додавання атрибутів примітивних типів як параметри запиту може бути необхідним результатом, якщо екземпляр моделі
був підготовлений спеціально під перенаправлення. Однак у анотованих контролерах модель може містити додаткові
атрибути, додані для цілей візуалізації (наприклад, значення полів, що випадають). Щоб уникнути появи таких
атрибутів в URL-адресі, метод, позначений анотацією @RequestMapping
, може оголосити аргумент типу
RedirectAttributes
і використовувати його для зазначення точних атрибутів, які мають бути доступні
RedirectView
. Якщо метод виконує перенаправлення, використовується вміст
RedirectAttributes
. В іншому випадку використовується вміст моделі.
RequestMappingHandlerAdapter
надає прапор ignoreDefaultModelOnRedirect
, який можна
використовувати, щоб вказати, що вміст Model
за замовчуванням в жодному разі не слід використовувати
при перенаправленні методом контролера. Натомість метод контролера повинен оголошувати атрибут типу RedirectAttributes
або, якщо він цього не робить, то ніякі атрибути не слід передавати RedirectView
. І в просторі імен
MVC, і в конфігурації MVC на Java цей прапор встановлено в false
, щоб зберегти зворотну сумісність.
Однак для нових програм ми рекомендуємо встановити значення true
.
Зверни увагу, що змінні шаблону URI-ідентифікаторів з цього запиту автоматично стають доступними при розширенні
URL-адреси перенаправлення, тому явно додавати їх через Model
або RedirectAttributes
не потрібно. У цьому прикладі показано, як визначити перенаправлення:
@PostMapping("/files/{path}")
public String upload(...) {
// ...
return "redirect:files/{path}";
}
@PostMapping("/files/{path}")
fun upload(... ): String {
// ...
return "redirect:files/{path}"
}
Іншим способом передачі даних до мети перенаправлення є використання flash атрибутів. На відміну від інших атрибутів перенаправлення, тимчасово flash атрибути зберігаються в HTTP-сесії (і, отже, не відображаються в URL-адресі).
Flash атрибути
Flash атрибути забезпечують можливість зберігати атрибути одного запиту, призначені для використання в іншому запиті.
Найчастіше це необхідно при
перенаправленні — наприклад, шаблон Post-Redirect-Get. Flash атрибути тимчасово зберігаються перед
перенаправленням (зазвичай у сесії), щоб вони були доступні для запиту після перенаправлення, після чого відразу
ж видаляються. Spring MVC має дві основні абстракції для підтримки flash атрибутів. FlashMap
використовується для зберігання flash атрибутів, а FlashMapManager
— для зберігання, отримання та
управління екземплярами FlashMap
.
Підтримка flash атрибутів завжди "ввімкнена" і не вимагає явної активації. Однак, якщо вона не використовується,
це ніколи не призводить до створення HTTP-сесії. При кожному запиті існує "вхідний" FlashMap
з
атрибутами, що передалися з попереднього запиту (якщо є), і "вихідний" FlashMap
з атрибутами, які
необхідно зберегти для наступного запиту. Обидва екземпляри FlashMap
доступні з будь-якої точки
Spring MVC через статичні методи в RequestContextUtils
.
Анотованим контролерам зазвичай не потрібно безпосередньо працювати з FlashMap
. Натомість
метод, анотований @RequestMapping
, може приймати аргумент типу RedirectAttributes
та
використовувати його для додавання flash атрибутів для сценарію перенаправлення. Flash атрибути, додані через
RedirectAttributes
, автоматично поширюються на "вихідний" FlashMap. Аналогічно, після
перенаправлення атрибути з "вхідного" FlashMap
автоматично додаються до Model
контролера, який обслуговує цільову URL-адресу.
Багатокомпонентність
Після активації MultipartResolver
вміст POST-запитів з multipart/form-data
парситься і
стає доступним як звичайні параметри запиту. У наступному прикладі здійснюється доступ до одного поля звичайної
форми та одного завантаженого файлу:
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(@RequestParam("name") String name,
@RequestParam("file") MultipartFile file) {
if (!file.isEmpty()) {
byte[] bytes = file.getBytes();
// store the bytes деякий час
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
@Controller
class FileUploadController {
@PostMapping("/form")
fun handleFormUpload(@RequestParam("name") name: String,
@RequestParam("file") file: MultipartFile): String {
if (!file.isEmpty) {
val bytes = file.bytes
// store the bytes деякий час
return "redirect:uploadSuccess"
}
return "redirect:uploadFailure"
}
}
Оголошення типу аргументу як List<MultipartFile>
уможливлює дозвіл кількох файлів для одного і
того ж імені параметра.
Якщо анотація @RequestParam
оголошена як Map<String, MultipartFile>
або MultiValueMap<String,
MultipartFile>
без імені параметра, вказаного в анотації, то Map заповнюється багатокомпонентними
файлами для кожного зазначеного імені параметра.
При багатокомпонентному парсингу на Servlet 3.0 також можна оголосити javax.servlet.http.Part
замість
MultipartFile
зі Spring як аргумент методу або типу значення колекції.
Ще можна використовувати багатокомпонентний вміст як елемент прив'язки даних до об'єкта команди. Наприклад, поле форми та файл з попереднього прикладу можуть бути полями об'єкта форми, як показано в наступному прикладі:
class MyForm {
private String name;
private MultipartFile file;
// ...
}
@Controller
public class FileUploadController {
@PostMapping("/form")
public String handleFormUpload(MyForm form, BindingResult errors) {
if (!form.getFile().isEmpty()) {
byte[] bytes = form.getFile().getBytes();
// store the bytes деякий час
return "redirect:uploadSuccess";
}
return "redirect:uploadFailure";
}
}
class MyForm(val name: String, val file: MultipartFile, ...)
@Controller
class FileUploadController {
@PostMapping("/form")
fun handleFormUpload(form: MyForm, errors: BindingResult): String {
if (!form.file.isEmpty) {
val bytes = form.file.bytes
// store the bytes деякий час
return "redirect:uploadSuccess"
}
return "redirect:uploadFailure"
}
}
Багатокомпонентні запити також можуть надсилатися від небраузерних клієнтів у сценарії c RESTful-сервісом. У цьому прикладі показаний файл з JSON:
POST /someUrl Content-Type: multipart/mixed --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="meta-data" Content-Type: application/json; charset=UTF-8 Content-Transfer-Encoding: 8bit { "name": "value" } --edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp Content-Disposition: form-data; name="file-data"; filename="file.properties" Content-Type: text/xml Content-Transfer-Encoding: 8bit ... File Data ...
Можна отримати доступ до частини "метаданих" за допомогою анотації @RequestParam
як до
String
,
але тобі, ймовірно, захочеться, щоб вона була десеріалізована з JSON (аналогічно анотації @RequestBody
).
Використовуй анотацію @RequestPart
для отримання доступу до багатокомпонентного файлу
після його перетворення за допомогою HttpMessageConverter:
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
@RequestPart("file-data") MultipartFile file) {
// ...
}
@PostMapping("/")
fun handle(@RequestPart("meta-data") metadata: MetaData,
@RequestPart("file-data") file: MultipartFile): String {
// ...
}
Ти можеш використовувати анотацію @RequestPart
у комбінації з javax.validation.Valid
або використовувати анотацію @Validated
зі Spring, які викликають застосування стандартної Bean
Validation. За замовчуванням помилки валідації призводять до появи винятку
MethodArgumentNotValidException
, який перетворюється на відповідь 400 (BAD_REQUEST). Як варіант,
можна обробляти помилки валідації локально в контролері через аргумент Errors
або
BindingResult
,
як показано в наступному прикладі:
@PostMapping("/")
public String handle(@Valid @RequestPart("meta-data") MetaData metadata,
BindingResult result) {
// ...
}
@PostMapping("/")
fun handle(@Valid @RequestPart("meta-data") metadata: MetaData,
result: BindingResult): String {
// ...
}
@RequestBody
Анотацію @RequestBody
можна використовувати для того, щоб тіло запиту можна було прочитати та
десеріалізувати в Object
через HttpMessageConverter
. У цьому прикладі використовується
аргумент з анотацією @RequestBody
:
@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
// ...
}
@PostMapping("/accounts")
fun handle(@RequestBody account: Account) {
// ...
}
Опцію Message Converters у конфігурації MVC можна використовувати для конфігурування або налаштування перетворення повідомлень.
Можна використовувати анотацію @RequestBody
у поєднанні з javax.validation.Valid
або
анотацією @Validated
зі Spring, які викликають застосування стандартної Bean Validation. За
замовчуванням помилки валідації призводять до появи винятку MethodArgumentNotValidException
, який
перетворюється на відповідь 400 (BAD_REQUEST). Як варіант, можна обробляти помилки валідації локально в
контролері через аргумент Errors
або BindingResult
, як показано в наступному прикладі:
@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, BindingResult result) {
// ...
}
@PostMapping("/accounts")
fun handle(@Valid @RequestBody account: Account, result: BindingResult) {
// ...
}
HttpEntity
Клас HttpEntity
більш-менш ідентичний у плані використання анотації @RequestBody
,
але заснований на об'єкті-контейнері, який відкриває заголовки та тіло запиту. У наступному лістингу
наведено приклад:
@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
// ...
}
@PostMapping("/accounts")
fun handle(entity: HttpEntity<Account>) {
// ...
}
@ResponseBody
Аннотацію @ResponseBody
можна використовувати для методу, щоб повернення серіалізувалося до тіла
відповіді через HttpMessageConverter. У наступному лістингу наведено приклад:
@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
// ...
}
@GetMapping("/accounts/{id}")
@ResponseBody
fun handle(): Account {
// ...
}
@ResponseBody
також підтримується на рівні класу. У цьому випадку вона успадковується всіма
методами контролера. Ця дія анотації @RestController
, яка є нічим іншим, як метаанотацією,
позначеною анотаціями @Controller
та @ResponseBody.
Можна використовувати @ResponseBody
з реактивними типами.
Можна використовувати опцію Message Converters у конфігурації MVC для конфігурування або налаштування перетворення повідомлень.
Можна поєднувати методи, позначені анотацією @ResponseBody
, з поданнями серіалізації у
форматі JSON.
ResponseEntity
Клас ResponseEntity
подібний до анотації @ResponseBody
, але зі статусом і
заголовками. Наприклад:
@GetMapping("/something")
public ResponseEntity<String> handle() {
String body = ...;
String etag = ...;
return ResponseEntity.ok().eTag(etag).body(body);
}
@GetMapping("/something")
fun handle(): ResponseEntity<String> {
val body = ...
val etag = ...
return ResponseEntity.ok().eTag(etag).build(body)
}
Spring MVC підтримує використання реактивного типу з одним значенням для асинхронного створення
ResponseEntity
,
та/або реактивних типів з одним і кількома значеннями для тіла. Це дозволяє використовувати такі типи
асинхронних відповідей:
-
ResponseEntity<Mono<T>>
абоResponseEntity<Flux<T>>
негайно повідомляють статус і заголовки відповіді, в той час як тіло відповіді надається пізніший момент. ВикористовуйMono
, якщо тіло складається з 0..1 значень, абоFlux,
якщо воно може створювати декілька значень. -
Mono<ResponseEntity<T>>
передбачає всі три параметри — статус відповіді, заголовки та тіло — асинхронно в пізніший момент. Це дозволяє змінювати статус відповіді та заголовки залежно від результатів асинхронної обробки запиту.
Jackson JSON
Spring передбачає підтримку бібліотеки Jackson JSON.
Подання у форматі JSON
Spring MVC забезпечує вбудовану підтримку засобу серіалізації подання Jackson's Serialization
Views, що дозволяє відображати лише підмножину всіх полів об'єкта. Щоб використовувати його з методами
контролера, анотованими @ResponseBody
, або класом ResponseEntity
, можна
використовувати анотацію @JsonView
з Jackson для активації класу представлення серіалізації, як
показано в наступному прикладі:
@RestController
public class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView.class)
public User getUser() {
return new User("eric", "7!jd#h23");
}
}
public class User {
public interface WithoutPasswordView {};
public interface WithPasswordView extends WithoutPasswordView {};
private String username;
private String password;
public User() {
}
public User(String username, String password) {
this.username = username;
this.password = password;
}
@JsonView(WithoutPasswordView.class)
public String getUsername() {
return this.username;
}
@JsonView(WithPasswordView.class)
public String getPassword() {
return this.password;
}
}
@RestController
class UserController {
@GetMapping("/user")
@JsonView(User.WithoutPasswordView::class)
fun getUser() = User("eric", "7!jd#h23")
}
class User(
@JsonView(WithoutPasswordView::class) val username: String,
@JsonView(WithPasswordView::class) val password: String) {
interface WithoutPasswordView
interface WithPasswordView : WithoutPasswordView
}
@JsonView
дозволяє використовувати масив класів подання,
але водночас можна задати тільки один для кожного методу контролера. Якщо потрібно активувати не одне подання, можна
використовувати інтерфейс.
Якщо потрібно зробити описане вище програмно, замість оголошення анотації @JsonView
огорни
значення, що повертається за допомогою MappingJacksonValue
і використовуй його для надання
подання серіалізації:
@RestController
public class UserController {
@GetMapping("/user")
public MappingJacksonValue getUser() {
User user = new User("eric", "7!jd#h23");
MappingJacksonValue value = новий MappingJacksonValue(user);
value.setSerializationView(User.WithoutPasswordView.class);
return value;
}
}
@RestController
class UserController {
@GetMapping("/user")
fun getUser(): MappingJacksonValue {
val value = MappingJacksonValue(User("eric", "7!jd#h23"))
value.serializationView = User.WithoutPasswordView::class.java
return value
}
}
Для контролерів, які використовують розпізнавання подання можна додати клас подання серіалізації до моделі, як показано в наступному прикладі:
@Controller
public class UserController extends AbstractController {
@GetMapping("/user")
public String getUser(Model model) {
model.addAttribute("user", new User("eric", "7!jd#h23"));
model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);
return "userView";
}
}
import org.springframework.ui.set
@Controller
class UserController : AbstractController() {
@GetMapping("/user")
fun getUser(model: Model): String {
model["user"] = User("eric", "7!jd#h23")
model[JsonView::class.qualifiedName] = User.WithoutPasswordView::class.java
return "userView"
}
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ