Spring Restful Web Services Beispiel mit JSON, Jackson und Client-Programm

Spring ist eines der am weitesten verbreiteten Java EE-Frameworks. Wir haben zuvor gesehen, wie man Spring MVC verwendet, um Java-basierte Webanwendungen zu erstellen. Heute werden wir lernen, Spring Restful Web Services mit Spring MVC zu erstellen und es dann mit dem Rest-Client zu testen. Abschließend werden wir auch untersuchen, wie man den Spring Restful Web Service mit der Spring RestTemplate API aufruft.

Spring REST

Wir werden die neueste Version von Spring 4.0.0.RELEASE verwenden und die Integration von Spring Jackson JSON nutzen, um JSON-Antworten im REST-Aufruf zu senden. Das Tutorial ist entwickelt worden in der Spring STS IDE, um leicht den Spring MVC Skelettcode zu erstellen und dann zu erweitern, um eine RESTful-Architektur zu implementieren. Erstellen Sie ein neues Spring MVC-Projekt in der STS, unser Endprojekt wird wie das unten stehende Bild aussehen. Wir werden uns jeden der Komponenten einzeln ansehen.

Spring REST Konfigurations-XML-Dateien

Unsere pom.xml-Datei sieht wie folgt aus.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>SpringRestExample</artifactId>
	<name>SpringRestExample</name>
	<packaging>war</packaging>
	<version>1.0.0-BUILD-SNAPSHOT</version>
	<properties>
		<java-version>1.6</java-version>
		<org.springframework-version>4.0.0.RELEASE</org.springframework-version>
		<org.aspectj-version>1.7.4</org.aspectj-version>
		<org.slf4j-version>1.7.5</org.slf4j-version>
		<jackson.databind-version>2.2.3</jackson.databind-version>
	</properties>
	<dependencies>
		<!-- Jackson -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>${jackson.databind-version}</version>
		</dependency>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${org.springframework-version}</version>
			<exclusions>
				<!-- Exclude Commons Logging in favor of SLF4j -->
				<exclusion>
					<groupId>commons-logging</groupId>
					<artifactId>commons-logging</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${org.springframework-version}</version>
		</dependency>

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>

		<!-- Logging -->
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>${org.slf4j-version}</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>jcl-over-slf4j</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>${org.slf4j-version}</version>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
			</exclusions>
			<scope>runtime</scope>
		</dependency>

		<!-- @Inject -->
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>

		<!-- Servlet -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<version>2.5</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.1</version>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- Test -->
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.7</version>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-eclipse-plugin</artifactId>
				<version>2.9</version>
				<configuration>
					<additionalProjectnatures>
						<projectnature>org.springframework.ide.eclipse.core.springnature</projectnature>
					</additionalProjectnatures>
					<additionalBuildcommands>
						<buildcommand>org.springframework.ide.eclipse.core.springbuilder</buildcommand>
					</additionalBuildcommands>
					<downloadSources>true</downloadSources>
					<downloadJavadocs>true</downloadJavadocs>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.5.1</version>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
					<compilerArgument>-Xlint:all</compilerArgument>
					<showWarnings>true</showWarnings>
					<showDeprecation>true</showDeprecation>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.codehaus.mojo</groupId>
				<artifactId>exec-maven-plugin</artifactId>
				<version>1.2.1</version>
				<configuration>
					<mainClass>org.test.int1.Main</mainClass>
				</configuration>
			</plugin>
		</plugins>
		
	</build>
</project>

Das STS-Tool generiert die pom.xml-Datei für uns. Allerdings habe ich das Spring Framework, AspectJ, SLF4J und Jackson-Version auf die neueste aktualisiert. Der größte Teil ist gemeinsam und wird automatisch generiert, wichtig ist zu beachten, dass ich Jackson JSON-Bibliotheken in die Abhängigkeit hinzugefügt habe, weil wir sie verwenden werden, um Objekte in JSON und umgekehrt zu konvertieren.

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="https://java.sun.com/xml/ns/javaee"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://java.sun.com/xml/ns/javaee https://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>
	
	<!-- Creates the Spring Container shared by all Servlets and Filters -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>
		
	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>

</web-app>

Diese Datei wird automatisch generiert und ich habe darin nichts geändert. Wenn Sie jedoch die Konfigurationsdateien für den Kontext und deren Speicherort ändern möchten, können Sie dies in der web.xml-Datei tun.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- Root Context: defines shared resources visible to all other web components -->
		
</beans>

Diese Datei enthält die gemeinsam genutzten Ressourcen, die für alle Webkomponenten sichtbar sein werden. Wir entwickeln einen einfachen REST-Service und deshalb habe ich hier nichts geändert.

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="https://www.springframework.org/schema/mvc"
	xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xmlns:beans="https://www.springframework.org/schema/beans"
	xmlns:context="https://www.springframework.org/schema/context"
	xsi:schemaLocation="https://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
		https://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
		https://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

	<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
	
	<!-- Enables the Spring MVC @Controller programming model -->
	<annotation-driven />

	<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
	<resources mapping="/resources/**" location="/resources/" />

	<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
	<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>
	
	<!-- Configure to plugin JSON as request and response in method handler -->
	<beans:bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
		<beans:property name="messageConverters">
			<beans:list>
				<beans:ref bean="jsonMessageConverter"/>
			</beans:list>
		</beans:property>
	</beans:bean>
	
	<!-- Configure bean to convert JSON to POJO and vice versa -->
	<beans:bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
	</beans:bean>	
	
	<context:component-scan base-package="com.journaldev.spring.controller" />
	
</beans:beans>

Der größte Teil wird automatisch generiert und enthält Standardkonfigurationen. Wichtige Punkte sind jedoch das annotation-driven-Element zur Unterstützung von konfigurierter Annotation und das Einbinden des MappingJackson2HttpMessageConverter in die messageConverters des RequestMappingHandlerAdapter, damit die Jackson-API aktiviert wird und JSON in Java-Beans und umgekehrt konvertiert. Durch diese Konfiguration verwenden wir JSON im Anforderungskörper und erhalten JSON-Daten in der Antwort.

Spring REST Modelklassen

Lassen Sie uns eine einfache POJO-Klasse schreiben, die als Eingabe und Ausgabe für unsere RESTful-Webservice-Methoden dient.

package com.journaldev.spring.model;

import java.io.Serializable;
import java.util.Date;

import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.DateSerializer;

public class Employee implements Serializable{

	private static final long serialVersionUID = -7788619177798333712L;
	
	private int id;
	private String name;
	private Date createdDate;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
	@JsonSerialize(using=DateSerializer.class)
	public Date getCreatedDate() {
		return createdDate;
	}
	public void setCreatedDate(Date createdDate) {
		this.createdDate = createdDate;
	}
	
	
}

Der einzige wichtige Punkt ist die Verwendung der @JsonSerialize-Annotation, um die Klasse DateSerializer für die Konvertierung von Datumsangaben vom Java-Typ in das JSON-Format und umgekehrt zu verwenden.

Spring Restful Web Service Endpunkte

Wir werden die folgenden REST-Webdienst-Endpunkte haben.

Sl. No URI HTTP Method Details
1 /rest/emp/dummy GET Health Check service, to insert a dummy data in the Employees data storage
2 /rest/emp/{id} GET To get the Employee object based on the id
3 /rest/emps GET To get the list of all the Employees in the data store
4 /rest/emp/create POST To create the Employee object and store it
5 /rest/emp/delete/{id} PUT To delete the Employee object from the data storage based on the id

Wir haben eine Klasse, die all diese URIs als Zeichenfolgenkonstanten definiert.

package com.journaldev.spring.controller;

public class EmpRestURIConstants {

	public static final String DUMMY_EMP = "/rest/emp/dummy";
	public static final String GET_EMP = "/rest/emp/{id}";
	public static final String GET_ALL_EMP = "/rest/emps";
	public static final String CREATE_EMP = "/rest/emp/create";
	public static final String DELETE_EMP = "/rest/emp/delete/{id}";
}

Spring Restful Web Service Controller-Klasse

Unsere EmployeeController-Klasse wird alle oben genannten Webdienst-Endpunkte veröffentlichen. Schauen wir uns den Code der Klasse an und dann werden wir jeden der Methoden im Detail kennenlernen.

package com.journaldev.spring.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.journaldev.spring.model.Employee;

/**
 * Handles requests for the Employee service.
 */
@Controller
public class EmployeeController {
	
	private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);
	
	// Map zum Speichern von Mitarbeitern, idealerweise sollten wir eine Datenbank verwenden
	Map empData = new HashMap();
	
	@RequestMapping(value = EmpRestURIConstants.DUMMY_EMP, method = RequestMethod.GET)
	public @ResponseBody Employee getDummyEmployee() {
		logger.info("Start getDummyEmployee");
		Employee emp = new Employee();
		emp.setId(9999);
		emp.setName("Dummy");
		emp.setCreatedDate(new Date());
		empData.put(9999, emp);
		return emp;
	}
	
	@RequestMapping(value = EmpRestURIConstants.GET_EMP, method = RequestMethod.GET)
	public @ResponseBody Employee getEmployee(@PathVariable("id") int empId) {
		logger.info("Start getEmployee. ID="+empId);
		
		return empData.get(empId);
	}
	
	@RequestMapping(value = EmpRestURIConstants.GET_ALL_EMP, method = RequestMethod.GET)
	public @ResponseBody List getAllEmployees() {
		logger.info("Start getAllEmployees.");
		List emps = new ArrayList();
		Set empIdKeys = empData.keySet();
		for(Integer i : empIdKeys){
			emps.add(empData.get(i));
		}
		return emps;
	}
	
	@RequestMapping(value = EmpRestURIConstants.CREATE_EMP, method = RequestMethod.POST)
	public @ResponseBody Employee createEmployee(@RequestBody Employee emp) {
		logger.info("Start createEmployee.");
		emp.setCreatedDate(new Date());
		empData.put(emp.getId(), emp);
		return emp;
	}
	
	@RequestMapping(value = EmpRestURIConstants.DELETE_EMP, method = RequestMethod.PUT)
	public @ResponseBody Employee deleteEmployee(@PathVariable("id") int empId) {
		logger.info("Start deleteEmployee.");
		Employee emp = empData.get(empId);
		empData.remove(empId);
		return emp;
	}
	
}

Für Einfachheit speichere ich alle Mitarbeiterdaten in der HashMap empData. Die @RequestMapping-Annotation wird verwendet, um die Anforderungs-URI auf die Handler-Methode abzubilden. Wir können auch die HTTP-Methode angeben, die von der Clientanwendung verwendet werden soll, um die REST-Methode aufzurufen. Die @ResponseBody-Annotation wird verwendet, um das Antwortobjekt im Antwortkörper abzubilden. Sobald das Antwortobjekt von der Handler-Methode zurückgegeben wird, wird MappingJackson2HttpMessageConverter aktiviert und wandelt es in eine JSON-Antwort um. Die @PathVariable-Annotation ist der einfache Weg, um die Daten aus der REST-URI zu extrahieren und sie dem Methodenargument zuzuordnen. Die @RequestBody-Annotation wird verwendet, um die Anforderungsbody-JSON-Daten in das Employee-Objekt abzubilden, auch dies geschieht durch das Mapping des MappingJackson2HttpMessageConverter. Der Rest des Codes ist einfach und selbsterklärend, unsere Anwendung ist bereit für Bereitstellung und Tests. Exportieren Sie einfach als WAR-Datei und kopieren Sie sie in das Verzeichnis der Webanwendung des Servlet-Containers. Wenn der Server in der STS konfiguriert ist, können Sie ihn einfach auf dem Server ausführen, um ihn bereitzustellen. Ich benutze WizTools RestClient, um die REST-Aufrufe aufzurufen, aber Sie können auch die Chrome-Erweiterung Postman verwenden. Die untenstehenden Screenshots zeigen die verschiedenen Aufrufe der von unserer Anwendung freigegebenen REST-APIs und deren Ausgabe. Gesundheitscheck – Dummy-Mitarbeiter-Rest-Aufruf Mitarbeiter erstellen POST Rest Call: Stellen Sie sicher, dass der Anforderungs-Content-Type auf „application/json“ gesetzt ist, sonst erhalten Sie den HTTP-Fehlercode 415. Mitarbeiter abrufen Rest Call Mitarbeiter löschen Rest Call Alle Mitarbeiter abrufen Rest Call

Spring Rest-Client-Programm

Rest-Clients sind gut, um unseren Rest-Webdienst zu testen, aber meistens müssen wir Rest-Dienste über unser Programm aufrufen. Wir können Spring RestTemplate verwenden, um diese Methoden einfach aufzurufen. Unten ist ein einfaches Programm, das unsere Anwendungsrest-Methoden mithilfe der RestTemplate-API aufruft.

package com.journaldev.spring;

import java.util.LinkedHashMap;
import java.util.List;

import org.springframework.web.client.RestTemplate;

import com.journaldev.spring.controller.EmpRestURIConstants;
import com.journaldev.spring.model.Employee;

public class TestSpringRestExample {

	public static final String SERVER_URI = "https://localhost:9090/SpringRestExample";
	
	public static void main(String args[]){
		
		testGetDummyEmployee();
		System.out.println("*****");
		testCreateEmployee();
		System.out.println("*****");
		testGetEmployee();
		System.out.println("*****");
		testGetAllEmployee();
	}

	private static void testGetAllEmployee() {
		RestTemplate restTemplate = new RestTemplate();
		// Wir können keine List erhalten, weil der JSON-Konverter den Typ von
		// Objekt in der Liste nicht kennt und es daher in den Standard-JSON-Objekttyp LinkedHashMap konvertiert
		List emps = restTemplate.getForObject(SERVER_URI+EmpRestURIConstants.GET_ALL_EMP, List.class);
		System.out.println(emps.size());
		for(LinkedHashMap map : emps){
			System.out.println("ID="+map.get("id")+",Name="+map.get("name")+",CreatedDate="+map.get("createdDate"));;
		}
	}

	private static void testCreateEmployee() {
		RestTemplate restTemplate = new RestTemplate();
		Employee emp = new Employee();
		emp.setId(1);emp.setName("Pankaj Kumar");
		Employee response = restTemplate.postForObject(SERVER_URI+EmpRestURIConstants.CREATE_EMP, emp, Employee.class);
		printEmpData(response);
	}

	private static void testGetEmployee() {
		RestTemplate restTemplate = new RestTemplate();
		Employee emp = restTemplate.getForObject(SERVER_URI+"/rest/emp/1", Employee.class);
		printEmpData(emp);
	}

	private static void testGetDummyEmployee() {
		RestTemplate restTemplate = new RestTemplate();
		Employee emp = restTemplate.getForObject(SERVER_URI+EmpRestURIConstants.DUMMY_EMP, Employee.class);
		printEmpData(emp);
	}
	
	public static void printEmpData(Employee emp){
		System.out.println("ID="+emp.getId()+",Name="+emp.getName()+",CreatedDate="+emp.getCreatedDate());
	}
}

Der größte Teil des Programms ist einfach zu verstehen. Wenn jedoch eine Rest-Methode aufgerufen wird, die eine Sammlung zurückgibt, müssen wir LinkedHashMap verwenden, weil die Konvertierung von JSON in Objekt nichts über das Employee-Objekt weiß und es in die Sammlung von LinkedHashMap konvertiert. Wir können eine Hilfsmethode schreiben, um von LinkedHashMap in unser Java Bean-Objekt zu konvertieren. Wenn wir das obige Programm ausführen, erhalten wir die folgende Ausgabe in der Konsole.

ID=9999,Name=Dummy,CreatedDate=Tue Mar 04 21:02:41 PST 2014
*****
ID=1,Name=Pankaj Kumar,CreatedDate=Tue Mar 04 21:02:41 PST 2014
*****
ID=1,Name=Pankaj Kumar,CreatedDate=Tue Mar 04 21:02:41 PST 2014
*****
2
ID=1,Name=Pankaj Kumar,CreatedDate=1393995761654
ID=9999,Name=Dummy,CreatedDate=1393995761381

Ein weiterer Punkt ist, dass die RestTemplate put-Methode keine Option zum Setzen des Antwortobjekts hat, da die PUT-Methode verwendet werden sollte, um etwas auf dem Server zu speichern, und ein einfacher HTTP-Statuscode 200 ausreichend sein sollte.

Projekt für Spring Restful Webservice herunterladen

Das ist alles für das Spring Restful Web Application Tutorial. Laden Sie das Beispieprojekt über den obigen Link herunter und probieren Sie es aus, um mehr zu lernen. UPDATE: Aufgrund vieler Anfragen, ein ähnliches Beispiel mit XML sowie Unterstützung sowohl für XML als auch JSON bereitzustellen, habe ich diese Anwendung in Spring REST XML JSON Example erweitert, um sowohl XML- als auch JSON-Anfragen und -Antworten zu unterstützen. Ich empfehle dringend, dies zu durchlaufen, um die Schönheit des Spring-Frameworks und wie einfach es ist, dies zu erreichen, zu sehen.

Sie können das vollständige Projekt von unserem GitHub Repository herunterladen.

Source:
https://www.digitalocean.com/community/tutorials/spring-rest-example-tutorial-spring-restful-web-services