Работа с JSON и XML (преобразование объектов) в Spring MVC. Формирование ответа и обработка запроса
Работа с JSON и XML (преобразование объектов) в Spring MVC. Формирование ответа и обработка запроса (RESTful веб-сервис). Применение RestTemplate в Spring MVC.
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
Используемые технологии и библиотеки
- Spring MVC 4.2.4.Release
- JAXB API 2.2.10
- IntelliJ IDEA 15.0.2
1. Описание задачи
В этой части будет рассмотрена работа приложения Spring MVC с форматами JSON и XML (RESTful веб-сервис). Будет показана возможность преобразовывать Java объекты в эти форматы, а так же формировать ответ в JSON или XML виде на запрос клиента. Для примера будет использован сторонний источник данных (внешний интернет ресурс), который будет предоставлять данные в том или ином формате. Таким образом здесь мы рассмотрим:
- Преобразование POJO (Plain Old Java Object) объектов в формат XML
- Преобразование POJO объектов в формат JSON
- Использование аннотаций @ResponseBody, @RestController, @XmlRootElement, @XmlElement
- Использование RestTemplate
2. Структура проекта
Классы находятся в пакете rest. Во вложенном пакете model находятся классы объектов, которые мы будем преобразовывать в XML или JSON формат (DBLogJSON, DBLogsXML, RestPostsModel, RestUserModel и т.д.). На уровень выше находятся два контроллера (RestController и RestTemplateController), которые будут демонстрировать различные способы работы с REST сервисами. Например, класс RestTemplateController обращается к внешнему сайту (http://jsonplaceholder.typicode.com), который возвращает данные в ответ на запросы, посылаемые нашим приложением.
Создан один файл представление — rest.jsp из одноименного пакета. На этой страничке будет доступен вызов того или иного метода из контроллеров.
3. pom.xml
Отдельной строкой была добавлена зависимость Java Architecture for XML Binding (JAXB). Этот пакет входит в Java SE и можно было бы его не добавлять для этого проекта, но здесь было выделить его в учебных целях.
1 2 3 4 5 6 |
<!-- JAXB XML Binding (@XmlElement, @XMLRoot)--> <dependency> <groupId>javax.xml.bind</groupId> <artifactId>jaxb-api</artifactId> <version>2.2.10</version> </dependency> |
Так же в предыдущих статьях была добавлена библиотека от Spring для работы с Rest JSON Processing API:
1 2 3 4 5 6 |
<!-- Spring REST jackson JSON Processing API --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.databind-version}</version> </dependency> |
Аннотации вроде @RestController доступны из общего пакета spring web mvc.
4. Конфигурация Spring MVC
В application-context была добавлена запись, которая описывает бин restTemplate. Описание этого класса будет немного ниже.
1 2 |
<!-- REST template configuration --> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/> |
application-context.xml:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> <!--find property file. See bean id='dataSource' for example ${jdbc.hsqldb.driverClass}--> <context:property-placeholder location="classpath:util.properties" /> <!-- XML Bean Definitions --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.hsqldb.driverClass}" /> <property name="url" value="${jdbc.hsqldb.url}" /> <property name="username" value="${jdbc.hsqldb.username}" /> <property name="password" value="${jdbc.hsqldb.password}" /> </bean> <!-- initialize Embedded DataSource. Встроенная база данных--> <jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:dbschema.sql"/> <jdbc:script location="classpath:test-data.sql"/> </jdbc:initialize-database> <!-- Java Mail Configuration --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="username" value="${java.mail.username}"/> <property name="password" value="${java.mail.password}"/> <property name="port" value="465"/> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">true</prop> <prop key="mail.smtp.starttls.enable">true</prop> <prop key="mail.smtp.starttls.required">true</prop> <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> <prop key="mail.smtp.host">${java.mail.host}</prop> </props> </property> </bean> <!-- Velocity Email Template Config Bean --> <bean id="velocityEngine" class="org.springframework.ui.velocity.VelocityEngineFactoryBean"> <property name="resourceLoaderPath" value="/WEB-INF/email-templates/"/> </bean> <!-- REST template configuration --> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"/> <!--Do not forget activate @Transactional JPA annotation with <annotation-driven/>--> <!-- JPA Persistence Context and EntityManager configuration --> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" > <!--packagesToScan - search Entity and mapping them --> <property name="packagesToScan"> <list> <value>ru.javastudy.mvcHtml5Angular.mvc.bean</value> <value>ru.javastudy.mvcHtml5Angular.mvc.rest.model</value> </list> </property> <property name="dataSource" ref="dataSource" /> <property name="jpaVendorAdapter"> <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" > <property name="generateDdl" value="true" /> <property name="showSql" value="true" /> </bean> </property> <property name="jpaProperties"> <props> <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.format_sql">false</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> </props> </property> </bean> <!-- Automatic Transaction Participation--> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <!--not uses but could autowired in JDBCExample bean--> <!-- <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> --> <!-- Quartz scheduling configuration --> <task:annotation-driven/> <!-- Quartz simple trigger --> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <property name="jobDetail" ref="simpleQuartzJob" /> <property name="repeatInterval" value="1000" /> <property name="startDelay" value="1000" /> </bean> <!-- Quartz cron trigger --> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="quartzCronJob"/> <property name="cronExpression" value="0/30 * * * * ?" /> <!-- every 30 seconds (seconds, minutes, hours, day of month, month, day of week, year(optional)) --> </bean> <!-- Quartz job --> <bean id="simpleQuartzJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="simpleQuartzTask" /> <property name="targetMethod" value="simpleTaskMethod" /> </bean> <!-- Quartz cron job --> <bean id="quartzCronJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="cronQuartzTask" /> <property name="targetMethod" value="cronTaskMethod" /> </bean> <!-- Quartz simple task --> <bean id="simpleQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quartz.QuartzTask" /> <!-- Quartz cron task --> <bean id="cronQuartzTask" class="ru.javastudy.mvcHtml5Angular.mvc.quartz.CronQuartzTask" /> <!-- Quartz Scheduler --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="jobDetails"> <list> <ref bean="simpleQuartzJob" /> <ref bean="quartzCronJob" /> </list> </property> <property name="triggers"> <list> <ref bean="simpleTrigger" /> <ref bean="cronTrigger" /> </list> </property> </bean> <!-- ScheduleTask example. Use <context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc.scheduling"/> and @Component on ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask or <bean id="scheduleTask" class="ru.javastudy.mvcHtml5Angular.mvc.scheduling.ScheduleTask"/> --> <!--End scheduling configuration --> </beans> |
В конфигурационный файл mvc-config.xml была добавлена настройка mvc:message-converters, в которой задан бин MappingJackson2HttpMessageConverter с атрибутом prettyPrint. Этот бин и атрибут позволяет получать JSON ответ в форматированном виде (легче читается человеком).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<!-- mvc:annotation-driven configures Spring MVC annotations Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values from @RequestMapping or @ExceptionHandler methods. --> <mvc:annotation-driven> <!--use int RestController to produce pretty json response--> <mvc:message-converters> <bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="prettyPrint" value="true" /> </bean> </mvc:message-converters> </mvc:annotation-driven> |
mvc-config.xml:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!--Spring security enabled annotations--> <security:global-method-security pre-post-annotations="enabled" secured-annotations="enabled" jsr250-annotations="enabled"/> <!-- context:component-scan This tag will scan @Component, @Repository, @Service, @Controller and also resolves @Autowired and @Qualifier --> <context:component-scan base-package="ru.javastudy.mvcHtml5Angular.mvc" /> <!-- mvc:annotation-driven configures Spring MVC annotations Support for validating @Controller inputs with @Valid, if a JSR-303 Provider is present on the classpath. HttpMessageConverter support for @RequestBody method parameters and @ResponseBody method return values from @RequestMapping or @ExceptionHandler methods. --> <mvc:annotation-driven> <!--use int RestController to produce pretty json response--> <mvc:message-converters> <bean id="jacksonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"> <property name="prettyPrint" value="true" /> </bean> </mvc:message-converters> </mvc:annotation-driven> <!-- activate @Transactional JPA annotation --> <tx:annotation-driven/> <!-- ViewResolver bean config for mapping strings to jsp views --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' --> <property name="order" value="1" /> <property name="prefix" value="/WEB-INF/view/" /> <property name="suffix" value=".jsp" /> </bean> <!-- File Upload bean config--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- set the maximum file size in bytes --> <property name="maxUploadSize" value="1000000"/> </bean> <!--Excel and PDF xml view configuration --> <!--disabling for jUnit test. --> <bean class="org.springframework.web.servlet.view.XmlViewResolver"> <property name="order" value="0" /> <property name="location" value="/WEB-INF/config/excel-pdf-config.xml"/> </bean> <mvc:view-controller path="/" view-name="/index"/> <mvc:view-controller path="/index.html" view-name="/index"/> <mvc:view-controller path="/login.html" view-name="/form/login"/> <mvc:view-controller path="/about.html" view-name="/about/about"/> <mvc:view-controller path="/file.html" view-name="/file/file"/> <mvc:view-controller path="/jdbc.html" view-name="/jdbc/jdbc"/> <mvc:view-controller path="/email.html" view-name="/email/email"/> <mvc:view-controller path="/rest.html" view-name="/rest/rest"/> <mvc:view-controller path="/orm.html" view-name="/orm/orm"/> <mvc:view-controller path="/jstl.html" view-name="/jstl/jstl"/> <mvc:view-controller path="/scope.html" view-name="/scope/scope"/> <mvc:view-controller path="/cookie.html" view-name="/cookie/cookieView"/> <mvc:view-controller path="/security.html" view-name="/security/security"/> <mvc:view-controller path="/security/admin.html" view-name="/security/admin"/> <!-- Static Resources Configuration (get access to static sources such as CSS and JavaScript files) --> <mvc:resources mapping="/resources/**" location="/resources/" /> <!-- themes can be put in different folder such as <mvc:resources mapping="/resources/**" location="/resources/themeBlue" /> <mvc:resources mapping="/resources/**" location="/resources/themeGreen" /> --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/interceptorCall/*"/> <!--need to use ' /** ' not ' /* ' if you want to intercept all requests.--> <!--<mvc:mapping path="/**"/>--> <bean class="ru.javastudy.mvcHtml5Angular.mvc.interceptors.SiteInterceptor"/> </mvc:interceptor> </mvc:interceptors> </beans> |
Каких-либо других настроек специально для работы с REST сервисами и преобразованием объектов в XML\JSON форматы не требуется.
5. Контроллер RestController
Для демонстрации работы по преобразованию обычных java классов в xml или JSON форматы был создан обычный Spring controller класс — RestController.
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
package ru.javastudy.mvcHtml5Angular.mvc.rest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogJSON; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogXML; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogsJSON; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogsXML; import ru.javastudy.mvcHtml5Angular.mvc.service.DBLogService; import java.util.List; /** * Created for JavaStudy.ru on 28.02.2016. */ @Controller public class RestController { @Autowired private DBLogService dbLogService; @RequestMapping(value = "/rest/getAllDBLogsXML", method = RequestMethod.GET, produces = "application/xml") public @ResponseBody DBLogsXML getAllDBLogsXML() { List<DBLogXML> dbLogsList = null; try { dbLogsList = dbLogService.queryAllDBLogsXML(); //JPA (Hibernate) // dbLogsList = dbLogService.queryAllDBLogsJDBCExampleXML(); //JDBC } catch (Exception e) { e.printStackTrace(); } System.out.println(dbLogsList); DBLogsXML dbLogsXML = new DBLogsXML(); dbLogsXML.setLogList(dbLogsList); return dbLogsXML; } /** look to mvc-config.xml for <mvc:message-converters>. It can produce 'pretty' json response. */ @RequestMapping(value = "/rest/getAllDBLogsJSON", method = RequestMethod.GET, produces = "application/json") public @ResponseBody DBLogsJSON getAllDBLogsJSON() { List<DBLogJSON> dbLogsJSONList = null; try { dbLogsJSONList = dbLogService.queryAllDBLogsJSON(); //JPA (Hibernate) // dbLogsJSONList = dbLogService.queryAllDBLogsJDBCExampleJSON(); //JDBC } catch (Exception e) { e.printStackTrace(); } System.out.println(dbLogsJSONList); DBLogsJSON dbLogsJSON = new DBLogsJSON(); dbLogsJSON.setLogList(dbLogsJSONList); return dbLogsJSON; } } |
Вначале указываем приложению, что этот класс является контроллером с помощью аннотации @Controller. Затем используя автосвязывание @Autowired получаем доступ к классу DBLogService. Этот сервисный класс предоставляет методы для доступа к данным из нашей встроенной in-memory H2 базы данных. Откуда берутся данные было описано в предыдущих статьях (смотрите Spring MVC и JDBC (Spring JDBC example). Подключение и настройка JDBC datasource, пример работы с JDBC в Spring).
DBLogService:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
package ru.javastudy.mvcHtml5Angular.mvc.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import ru.javastudy.mvcHtml5Angular.mvc.bean.DBLog; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogJSON; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogXML; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogsJSON; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.DBLogsXML; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.sql.DataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; /** * Created for JavaStudy.ru on 05.03.2016. */ @Service public class DBLogService { @PersistenceContext private EntityManager entityManager; /* or you can use JDBCTemplate instead JPA */ private JdbcTemplate jdbcTemplate; /* if use JDBCTemplate */ @Autowired public DBLogService (DataSource dataSource) { this.jdbcTemplate = new JdbcTemplate(dataSource); } //JPA XML example public List<DBLogXML> queryAllDBLogsXML() { System.out.println("DBLogService queryAllDBLogsXML() is called"); /*important! We use entity class DBLogXML instead table name LOG (not 'from LOG')*/ return entityManager.createQuery("from DBLogXML", DBLogXML.class).getResultList(); } //JPA JSON example public List<DBLogJSON> queryAllDBLogsJSON() { System.out.println("DBLogService queryAllDBLogsJSON() is called"); /*important! We use entity class DBLogJSON instead table name LOG (not 'from LOG')*/ return entityManager.createQuery("from DBLogJSON", DBLogJSON.class).getResultList(); } //JdbcTemplate query with in method RowMapper example (XML) public List<DBLogXML> queryAllDBLogsJDBCExampleXML() { System.out.println("DBLogService queryAllDBLogsJDBCExampleXML() is called"); final String querySQL = "SELECT * FROM LOG"; List<DBLogXML> dbLogs = jdbcTemplate.query(querySQL, new RowMapper<DBLogXML>() { @Override public DBLogXML mapRow(ResultSet resultSet, int rowNum) throws SQLException { DBLogXML dbLog = new DBLogXML(); dbLog.setIDLOG(resultSet.getInt("IDLOG")); dbLog.setLOGSTRING(resultSet.getString("LOGSTRING")); return dbLog; } }); return dbLogs; } //JdbcTemplate query with in method RowMapper example (JSON) public List<DBLogJSON> queryAllDBLogsJDBCExampleJSON() { System.out.println("DBLogService queryAllDBLogsJDBCExampleJSON() is called"); final String querySQL = "SELECT * FROM LOG"; List<DBLogJSON> dbLogs = jdbcTemplate.query(querySQL, new RowMapper<DBLogJSON>() { @Override public DBLogJSON mapRow(ResultSet resultSet, int rowNum) throws SQLException { DBLogJSON dbLog = new DBLogJSON(); dbLog.setIDLOG(resultSet.getInt("IDLOG")); dbLog.setLOGSTRING(resultSet.getString("LOGSTRING")); return dbLog; } }); return dbLogs; } } |
Над методами указывается атрибут produces, который указывает в какой формат данных должен быть преобразован ответ метода. Указание на то, что ответ должен быть преобразован в JSON или XML формат дает аннотация @ResponseBody. Теперь рассмотрим POJO классы объектов данных.
6. POJO классы
Для того, чтобы преобразование класса в XML формат прошло успешно, нам необходимо использовать аннотации @XmlRootElement и\или @XmlElement. Для JSON формата ничего дополнительно в классе указывать не нужно!
У нас используется несколько java классов, которые будут преобразовываться в xml или json ответ клиенту: DBLogJSON, DBLogsJSON, DBLogXML, DBLogsXML, RestPostsModel, RestUserModel. DBLog — класс, который имеет две переменных экземпляра — id, и строку (с описанием логов). Два последних класса RestPostModel и RestUserModel — классы, которые используются для отображения данных со стороннего ресурса и будут описаны в пункте по RestTemplate. Т.к. для JSON формата никаких аннотаций или дополнительных манипуляций с классом не требуется, то рассмотрим только классы с суффиксом XML.
DBLogXML:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package ru.javastudy.mvcHtml5Angular.mvc.rest.model; import javax.persistence.*; import javax.xml.bind.annotation.XmlElement; import java.io.Serializable; /** * Created for JavaStudy.ru on 26.02.2016. */ @Entity //use for JPA. For JDBC you can clear this @Table(name = "LOG") public class DBLogXML implements Serializable { private static final long serialVersionUID = 1L; @Column(name = "IDLOG") @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int IDLOG; @Column(name = "LOGSTRING") private String LOGSTRING; public DBLogXML() { } public DBLogXML(int idLog, String logString) { this.IDLOG = idLog; this.LOGSTRING = logString; } public int getIDLOG() { return IDLOG; } @XmlElement public void setIDLOG(int iDLOG) { IDLOG = iDLOG; } public String getLOGSTRING() { return LOGSTRING; } @XmlElement public void setLOGSTRING(String lOGSTRING) { LOGSTRING = lOGSTRING; } } |
Тут следует обратить внимание на аннотацию @XmlElement. С помощью нее класс будет преобразовывать переменные экземпляра в XML элемент. Остальные аннотации относятся к маппингу таблицы из базы данных на сущность. Если нам необходимо показать список элементов, то мы можем использовать аннотацию @XmlRootElement, который задаст корневой элемент, а далее с помощью указания @XmlElement будут отображены все объекты массива.
DBLogsXML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
package ru.javastudy.mvcHtml5Angular.mvc.rest.model; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; /** * Created for JavaStudy.ru on 05.03.2016. */ @XmlRootElement(name = "LOGS") public class DBLogsXML { private List<DBLogXML> logList; @XmlElement(name = "LOG") public List<DBLogXML> getLogList() { return logList; } public void setLogList(List<DBLogXML> logList) { this.logList = logList; } } |
Объекты с суффиксом JSON точно такие же, только без указания аннотаций относящихся к XML.
7. RestTemplate
Класс RestTemplate — служит для доступа к REST данным на удаленном веб сервере. Это своего рода аналог JDBCTemplate (Spring MVC и JDBC (Spring JDBC example). Подключение и настройка JDBC datasource, пример работы с JDBC в Spring). Напомню, что бин RestTemplate был прописан в файле конфигурации Spring в начале статьи. Мы будем использовать сгенерированный ответ со стороннего веб сервера (http://jsonplaceholder.typicode.com). Он будет генерировать объекты вроде «Посты пользователя» или «Пользователь (описание)». Контроллер, который будет демонстрировать работу с RestTemplate и обработку данных с внешнего веб сервера выглядит так:
RestTemplateController:
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
package ru.javastudy.mvcHtml5Angular.mvc.rest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.RestPostsModel; import ru.javastudy.mvcHtml5Angular.mvc.rest.model.RestUserModel; import java.util.Arrays; import java.util.List; /** * Created for JavaStudy.ru on 28.02.2016. */ @RestController //will add automatically the @ResponseBody annotation to all methods public class RestTemplateController { /** * Accessing a third-party REST service inside a Spring application * it can even bind that data to custom domain types. */ @Autowired private RestTemplate restTemplate; private final String EXTERNAL_REST_URL = "http://jsonplaceholder.typicode.com"; //free JSON services @RequestMapping(value = "/rest/users", method = RequestMethod.GET) public List<RestUserModel> getRestUsers() { System.out.println("RestTemplateController getRestUsers is called"); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0"); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); //JSON http://jsonplaceholder.typicode.com/users ResponseEntity<RestUserModel[]> response = restTemplate.exchange( EXTERNAL_REST_URL +"/users", HttpMethod.GET, entity, RestUserModel[].class ); return Arrays.asList(response.getBody()); } @RequestMapping(value = "/rest/posts", method = RequestMethod.GET) public List<RestPostsModel> getRestPosts() { System.out.println("RestTemplateController getRestPosts is called"); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0"); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); ResponseEntity<RestPostsModel[]> response = restTemplate.exchange( EXTERNAL_REST_URL +"/posts", HttpMethod.GET, entity, RestPostsModel[].class ); return Arrays.asList(response.getBody()); } @RequestMapping(value = "/rest/posts/{param}", method = RequestMethod.GET) public RestPostsModel getRestPostsById(@PathVariable("param") String param) { System.out.println("RestTemplateController getRestPostsById is called"); HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0"); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); ResponseEntity<RestPostsModel> response = restTemplate.exchange( EXTERNAL_REST_URL +"/posts/" + param, HttpMethod.GET, entity, RestPostsModel.class ); return response.getBody(); } //JSON Deletes a post @RequestMapping(value = "/rest/delPosts/{postId}", method = RequestMethod.GET) @ResponseStatus(value = HttpStatus.OK) public void deletePostByID(@PathVariable(value="postId") String postId) { HttpHeaders headers = new HttpHeaders(); headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON)); headers.set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:49.0) Gecko/20100101 Firefox/49.0"); HttpEntity<String> entity = new HttpEntity<String>("parameters", headers); //in test case 100 posts. Try to del id 100+ (id=150 for example) and check status in console restTemplate.exchange(EXTERNAL_REST_URL +"/posts/" + postId, HttpMethod.DELETE, entity, String.class); System.out.println("@RestTemplateControllerExample deletePostByID is called"); } @ExceptionHandler @ResponseStatus(value = HttpStatus.FORBIDDEN,reason="FORBIDDEN ACCESS (PROVIDE YOUR CUSTOM REASON HERE)") public void handleException(Exception ex) { System.out.println("@RestTemplateControllerExample handleException"); System.out.println(ex); } } |
Сразу можно обратить внимание на аннотацию @RestController. Такая запись автоматически добавляет аннотацию @ResponseBody во все методы внутри класса. Далее с помощью связывание мы получаем доступ к объекту RestTemplate, с помощью которого мы будем преобразовывать данные ответа внешнего сервера (который указан в переменной EXTERNAL_REST_URL).
Далее следует обратить внимание на метод restTemplate.getForEntity(). С помощью этого метода мы преобразовываем REST ответ сервера в нашу сущность.
1 2 3 4 |
ResponseEntity<RestPostsModel[]> response = restTemplate.getForEntity( EXTERNAL_REST_URL +"/posts", RestPostsModel[].class ); |
А вот и сам класс сущность для маппинга ответа с внешнего сервера. RestPostsModel:
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 27 28 29 30 31 32 33 34 35 36 37 |
package ru.javastudy.mvcHtml5Angular.mvc.rest.model; /** * Created for JavaStudy.ru on 28.02.2016. */ public class RestPostsModel { private String userId; private String id; private String title; private String body; public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getBody() { return body; } public void setBody(String body) { this.body = body; } } |
Как видите это просто обычный java класс с несколькими полями.
8. Запуск приложения
В этой части не буду приводить описание jsp страницы, т.к. на ней нет ничего отличного от предыдущих частей. Вы можете ее посмотреть скачав приложение в конце статьи. Перейдем к просмотру того, что у нас выдает наше приложение.
8.1. Преобразование объекта в XML\JSON
Так у нас выглядят объекты JSON и XML.
JSON (по кнопке Get DBLogs JSON):
XML (по кнопке Get DBLogs XML):
А так выглядит обработка результатов с помощью RestTemplate (кнопка Get Rest Users):
Так же вы могли заметить методы по возврату поста по ID, удаление поста. Эти методы демонстрируют работу других методов, которые могут быть использованы в RESTful веб сервисах. Их реализацию можете посмотреть внутри проекта.
Исходные коды
MVC_AngularJS_Html5 full project — полный проект Spring MVC + AngularJS + Bootstrap + HTML5.
16. Rest and JSON (JPA and JDBC) — код для этой части
Обзор приложения Spring MVC + AngularJS + Bootstrap + HTML5
713 thoughts on “Работа с JSON и XML (преобразование объектов) в Spring MVC. Формирование ответа и обработка запроса”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
Здравствуйте! Очень интересные статьи, все прочитал. Но не нашёл описания того, как получить и обработать данные в формате json, которые приходят из браузера. Как это можно сделать?
Существует достаточно много библиотек по преобразованию JSON в POJO. Можете посмотреть Jackson, Gson и т.д.
Выскакивает эта ошибка:
ExceptionHandler msg: org.springframework.web.client.HttpClientErrorException: 403 Forbidden
Как вы решили проблему?
Решено с помощью комментария Андрея ниже.
Обращение к автору статьи, уже 5 день с проблемой разбираюсь, ничего не помогает.
По порядку опишу что делал и что в итоге вышло. Сделал все как вы писали в статье, полностью весь код проверил несколько раз, все должно функционировать. Но, выкидывает 403 ошибку и все тут. То есть перехожу на rest.html , жмякаю на ссылку для того чтобы принять json с сервера и получаю 403 ошибку.
Думал что виноваты конфиги, перепроверил все несколько раз, говорят проблема может быть в отсутствии
но это в конфиге присутсвтует.
Далее я вообще закомментировал все настройки секьюрити, убрал фильтр в web.xml , удалил все зависимости из pom.xml , почистил любые сведения о секьюрити в jsp’шках. Бесполезно, 403 ошибка.
Дебаг ничего не дал, в метод контроллера запрос тупо не доходит.
Вот такая вот проблема, как можно решить?)
Попробуйте скачать исходник и его запустить (на томкате проверялся). Если работает, то потихоньку сравнивайте ваш код и проектный. Если не работает, то проверяйте окружение.
Исходник скачал, запустил, проблема та же самая. Что конкретно можно проверить, не подскажете?
К тому же стало понятно, что если обращаться к /rest/users например то крах происходит в данном месте
Можете не мучиться — сайт действительно больше не выдает ответ на код из этой статьи. Добавил комментарий вначале, чтобы другие не страдали:) Всё что нужно выяснить это что нужно той стороне, чтобы ответить на запрос (возможно добавить какую-то информацию в header или что-то такое). Кстати быстро глянул и на запрос по сайту из URL (jsonplaceholder.typicode.com) и комбинации с spring RestTemplate люди сталкивались с этой бедой, возможно там где-то есть решение.
Решено с помощью комментария Андрея.
Сайт jsonplaceholder.typicode.com проверяет http заголовки, в частности User-Agent.
Нужно немного поправить код:
Спасибо! Поправил код и текст в статье.