Пожалуй ни одно web приложение не обходится без форм для отправки данных на сервер. Рассмотрим как работает механизм web форм в Spring MVC.
Простая форма
Для начала рассмотрим обработку простой формы. Пусть эта форма, в качестве примера, отправляет на сервер имя человека, которое тот будет вводить в специальное поле. Это типовая задача, которая встречается повсеместно (регистрация пользователя в системе, покупателя в интернет магазине и т. д.). Обычно помимо имени вводят и другие сведения. Но в нашем примере мы пока ограничимся только именем.
В начале создадим класс, с которым будут сопоставляться данные формы. По сути, это будет её модель.
1 2 3 4 5 6 7 8 9 |
public class MansForm { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } |
Создадим саму форму для отправки данных. Форму мы будем создавать при помощи библиотеки тегов форм Spring. Как и все библиотеки серверных тегов она подключается сразу после тега page с помощью оператора taglib.
Теги из библиотек вызываются с помощью специального префикса. По традиции, для библиотеки тегов форм Spring задаётся префикс form.
Для создания формы нам потребуется тег form. В его атрибуте method укажем метод POST, а в modelAttribute название созданного нами класса с маленькой буквы.
Имя будем вводить при помощи серверного тега input, указав в атрибуте path имя поля, в котором хранится имя. Это очень важно, так как в противном случае Spring не сможет связать данные формы с моделью.
Как всё это выглядит в коде можно увидеть на приведённом ниже примере.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ taglib prefix="form" uri="https://www.springframework.org/tags/form"%> <!DOCTYPE html> <html lang="ru"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Введите Ваше имя</title> </head> <body> <h1>Введите Ваше имя.</h1> <form:form method="post" modelAttribute="mansForm" " lang="ru"> <table> <tr> <td>Имя:</td> <td><form:input path="name" /></td> </tr> <td colspan="3"><input type="submit" value="Submit" /></td> </tr> </table> </form:form> </body> </html> |
Теперь, когда у нас есть модель и представление с формой которая отправляет данные на сервер, нам необходимо на стороне сервера эти данные получить и обработать.
Для этого нам необходим соответствующий контроллер.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@Controller public class mansController { @RequestMapping(value = "/addman", method = RequestMethod.GET) protected ModelAndView addMan(HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception { ModelAndView result = new ModelAndView("index/addMan"); result.addObject("mansForm", new MansForm()); return result; } @RequestMapping(value = "/addman", method = RequestMethod.POST) protected String addManPost(MansForm mansForm) throws UnsupportedEncodingException { String manName = man.setName(mansForm.getName()); // Здесь выполняем какие-то действия (например, сохранение в БД) return "redirect:/addman"; } } |
Приведённый контроллер содержит два метода. Первый (addMan) отрабатывает при GET запросе и выводит созданную ранее страницу с формой. Второй же (addManPost) получает данные поступившие на сервер от формы и выполняет их обработку.
Данные формы принимаются методом addManPost в виде параметра mansForm.
Из поступивших данных, в качестве примера, в локальную переменную manNamer извлекается введённое имя. Вместо этого можно выполнить и более сложные действия. Например, валидацию (будет рассматриваться в отдельной статье) или сохранение в базе данных.
После завершения всех операций метод просто выполняет перенаправление (редирект) на страницу с исходной формой.
Мы изучили работу простых форм на примере единичного текстового поля. Но, даже если форма содержит другие поля (пароль, дата и т. д.), работа с ними полностью аналогична с поправкой на имена конкретных полей и типы данных.
Однако вышесказанное не относится к процедуре загрузки файлов на сервер.
Если форма содержит поля тип file их следует обрабатывать особым способом, который будет описан далее.
Форма с загрузкой файлов
Если форма предназначена для загрузки файлов, это затрагивает не только неё, но и на всё web приложение в целом.
Дело в том, что загруженные файлы необходимо где-то хранить. Загружать их в БД чаще всего не оправдано. Поэтому в качестве хранилища используется специальная папка, но о её существовании приложение ещё должно каким-то образом узнать.
Также для загрузки файлов необходимо подключить специальный компонент, который будет позволяет приложению обрабатывать такие формы.
Так что перед тем, как приступить непосредственно к реализации загрузки, нам нужно выполнить три подготовительных шага.
Первый — создание папки для хранения загружаемых файлов и назначение для неё необходимых прав (обычно всё это делается стандартными средствами операционной системы). Второй — описание этой папки в приложении, чтобы он смогло её найти и загружать туда файлы. Третий — подключение недостающего компонента.
Последнее можно сделать либо с помощью базы данных (например, хранить соответствующий путь в таблице настроек) либо с помощью JNDI.
В случае JNDI в папке META-INF создаётся файл context.xml, в котором путь к папке прописывается в директиве Environment, как это показано ниже.
1 2 3 4 |
<?xml version="1.0" encoding="UTF-8"?> <Context path="/mavenWebTest4"> <Environment name="fileStorage/basePath" value="C:\files\" type="java.lang.String"/> </Context> |
Атрибут name состоит из двух частей. Первая часть (fileStorage), говорит на о том, что данный параметр определяет папку, а второй, это собственно имя параметра.
Формат обращения к подобным JNDI параметрам будет описан позднее, когда мы будем добавлять поддержку загрузки файлов в контроллер.
Теперь подключим компонент для работы с загружаемыми файлами и настроим его.
Для этого добавим следующую зависимость для Maven:
1 2 3 4 5 |
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.1</version> </dependency> |
А, в файле dispatcher-servlet.xml добавим следующий компонент:
1 2 3 |
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="maxUploadSize" value="2097152" /> </bean> Параметр maxUploadSize задаёт максимальный размер загружаемого файла в байтах. В данном случае 2 МБ. |
Доработаем форму и её модель таким образом, чтобы с их помощью можно было передавать файлы на сервер.
В качестве примера, пусть у пользователя появится возможность загрузить свой аватар.
В Spring MVC загружаемые файлы представлены классом MultipartFile. Добавим в модель соответствующее поле для хранения файла аватара.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MansForm { private String name; private MultipartFile avatar; public String getName() { return name; } public void setName(String name) { this.name = name; } public MultipartFile getAvatar() { return avatar; } public void setAvatar(MultipartFile avatar) { this.avatar = avatar; } } |
Отредактируем форму.
У тега form добавим атрибут enctype, чтобы обеспечить поддержку передачи файлов. А, внутри формы создадим новый тег input, который будет иметь тип file. В атрибуте path этого тега укажем имя поля модели, в котором будет сохранён файл аватара для передачи на сервер.
Код страницы с доработанной формой приведён ниже.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
<%@page contentType="text/html" pageEncoding="UTF-8"%> <%@ taglib prefix="form" uri="https://www.springframework.org/tags/form"%> <!DOCTYPE html> <html lang="ru"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Введите Ваше имя</title> </head> <body> <h1>Введите Ваше имя.</h1> <form:form method="post" modelAttribute="mansForm" enctype="multipart/form-data" " lang="ru"> <table> <tr> <td>Имя:</td> <td><form:input path="name" /></td> <tr> <td>Фотография:</td> <td><form:input type="file" path="avatar" /></td> </tr> </tr> <td colspan="2"><input type="submit" value="Submit" /></td> </tr> </table> </form:form> </body> </html> |
В результате мы уже можем отправлять файлы на сервер. Но, нам ещё нужно их получить и сохранить в нашу папку, которую мы создали в самом начале.
Для того чтобы в контроллере получить путь папке для сохранения файлов достаточно объявить в нём строковое поле с аннотацией @Resource, указав в качестве параметра имя mappedName имя ранее созданного параметра JNDI.
Сам метод , который обрабатывает данные поступающие на сервер принципиально не изменится. Просто в него будет включено сохранение полученного файла в нужной папке.
Последнюю можно выполнить всего в одной строке кода при промощи метода transferTo класса MultipartFile, который переместит загруженный файл из временной папки в целевую.
Разумеется, что при сохранении в постоянной папке файл должен иметь соответствующее имя. Для этого к пути папки для сохранения, добавим его исходное имя, которое можно получить с помощью метода getOriginalFilename класса MultipartFile.
Ниже приведён код контроллера после всех доработок.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@Controller public class mansController { @Resource(mappedName = "fileStorage/basePath") private String basePath; @RequestMapping(value = "/addman", method = RequestMethod.GET) protected ModelAndView addMan(HttpServletRequest hsr, HttpServletResponse hsr1) throws Exception { ModelAndView result = new ModelAndView("index/addMan"); result.addObject("mansForm", new MansForm()); return result; } @RequestMapping(value = "/addman", method = RequestMethod.POST) protected String addManPost(MansForm mansForm) throws UnsupportedEncodingException { String manName = man.setName(mansForm.getName()); if (mansForm.getAvatar() != null) { MultipartFile file = mansForm.getAvatar(); try { String fileName = basePath + file.getOriginalFilename(); file.transferTo(new File(fileName)); } catch (IOException ex) { Logger.getLogger(mansController.class.getName()).log(Level.SEVERE, null, ex); } } return "redirect:/addman"; } } |
Стоит обратить внимание на то, что при работе с загруженным файлом нам не требуется ничего знать о его местоположении в временной папке и пр. Файл доступен сразу посредство соответствующих методов классов MultipartFile и модели формы.
После сохранения файла с ним можно, как вариант, сохранить путь к нему в базе данных, чтобы впоследствии отображать аватар рядом с именем пользователя на web странице.
Примеры, рассмотренные в этой статье выполнены с использованием Spring 5, но они вполне могут быть совместимы и с другими версиями Spring.
Добавить комментарий