RESTful Web Services Example in Java with Jersey, Spring
2016-07-15 12:54
711 查看
Looking to REST? In Java? There’s never time for that :), but if you are looking to use an
“architectural style consisting of a coordinated set of constraints applied to components, connectors, and data elements, within a distributed
hypermedia system” in Java, then you have come to the right place, because in this post I will present a simple RESTful API that maps REST calls to backend services offering
CRUD functionality.
via a REST API. It does CRUD operations on a single database table (Podcasts), triggered via the REST web services API. Though fairly simple, the example highlights the most common annotations you’ll need to build your own REST API.
Jersey RESTful Web Services in JAVA. The Jersey RESTful Web Services framework is open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a
JAX-RS (JSR 311 & JSR 339) Reference Implementation.
Spring, and this example is no exception. You’ll find out how Jersey 2 integrates with Spring.
Tomcat .
https://bitbucket.org/javarestfulwebservice/restful-jersey/src/f0a72f263d0281adf333bfcb71868ea69d931bb0?at=master
2.1.2.1. Jersey-servlet
Notice the Jersey servlet configuration [lines 18-33]. The
register it in my web application
documentation for other possibilities.
The implementation of
package com.npf.init;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
/**
*
* Registers the components to be used by the JAX-RS application
*
*
*/
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
/**
* which is a Spring filter that provides a bridge between JAX-RS and Spring request attributes
*/
register(RequestContextFilter.class);
/**
* which is a feature that registers Jackson JSON providers – you need it for the application to understand JSON data
*/
register(JacksonFeature.class);
/**
* scan the web service package
*/
packages("com.npf.web");
}
}
The class registers the following components
2.1.2.2. Spring application context configuration
The Spring application context configuration is located in the classpath under
<?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:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<import resource="classpath:spring/applicationContext-service.xml"/>
<import resource="classpath:spring/applicationContext-dao.xml"/>
<import resource="classpath:spring/applicationContext-aop.xml"/>
<context:component-scan base-package="com.npf"/>
</beans>
resources in our web API. Resources are the central concept in REST and are characterized by two main things:
each is referenced with a global identifier (e.g. a
URI in HTTP).
has one or more representations, that they expose to the outer world and can be manipulated with (we’ll be working mostly with JSON representations in this example)
The podcast resources are represented in our application by the
Podcast class:
package com.npf.model;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Podcast entity
*
*
*
* { "id":1, "title":"Quarks & Co - zum Mitnehmen-modified", "linkOnPodcastpedia":"http://www.podcastpedia.org/podcasts/1/Quarks-Co-zum-Mitnehmen", "feed":"http://podcast.wdr.de/quarks.xml", "description":"Quarks & Co: Das Wissenschaftsmagazin", "insertionDate":1388213547000 }
*
*/
@XmlRootElement
public class Podcast implements Serializable {
private static final long serialVersionUID = -8039686696076337053L;
/** id of the podcas */
private Long id;
/** title of the podcast */
private String title;
/** link of the podcast on Podcastpedia.org */
private String linkOnPodcastpedia;
/** url of the feed */
private String feed;
/** description of the podcast */
private String description;
/** when an episode was last published on the feed*/
private Date insertionDate;
public Podcast(){}
public Podcast(String title, String linkOnPodcastpedia, String feed,String description) {
this.title = title;
this.linkOnPodcastpedia = linkOnPodcastpedia;
this.feed = feed;
this.description = description;
}
public Podcast(Long id,String title, String linkOnPodcastpedia, String feed,String description) {
this.title = title;
this.linkOnPodcastpedia = linkOnPodcastpedia;
this.feed = feed;
this.description = description;
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLinkOnPodcastpedia() {
return linkOnPodcastpedia;
}
public void setLinkOnPodcastpedia(String linkOnPodcastpedia) {
this.linkOnPodcastpedia = linkOnPodcastpedia;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFeed() {
return feed;
}
public void setFeed(String feed) {
this.feed = feed;
}
public Date getInsertionDate() {
return insertionDate;
}
public void setInsertionDate(Date insertionDate) {
this.insertionDate = insertionDate;
}
}
The strucuture is pretty simple – there are an
As already mentioned the
package com.npf.web;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.npf.dao.PodcastDao;
import com.npf.model.Podcast;
@Path("/podcasts")
public class PodcastRestService {
@Autowired
private PodcastDao podcastDao;
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Transactional
public Response createPodcast(Podcast podcast) {
podcastDao.createPodcast(podcast);
return Response.status(201).entity("A new podcast/resource has been created").build();
}
@POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces({MediaType.TEXT_HTML}) @Transactional public Response createPodcastFromForm(@FormParam("title") String title, @FormParam("linkOnPodcastpedia") String linkOnPodcastpedia, @FormParam("feed") String feed, @FormParam("description") String description) { Podcast podcast = new Podcast(5L,title, linkOnPodcastpedia, feed, description); podcastDao.createPodcast(podcast); return Response.status(201).entity("A new podcast/resource has been created").build(); }
@POST @Path("list") @Consumes({MediaType.APPLICATION_JSON}) @Transactional public Response createPodcasts(List<Podcast> podcasts) { for(Podcast podcast : podcasts){ podcastDao.createPodcast(podcast); } return Response.status(204).build(); }
@GET @Path("{id}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response findById(@PathParam("id") Long id) { Podcast podcastById = podcastDao.getPodcastById(id); if(podcastById != null) { return Response.status(200).entity(podcastById).build(); } else { return Response.status(404).entity("The podcast with the id " + id + " does not exist").build(); } }
@PUT
@Path("{id}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response updatePodcastById(@PathParam("id") Long id, Podcast podcast) {
if(podcast.getId() == null) podcast.setId(id);
String message;
int status;
if(podcastDao.updatePodcast(podcast) == 1){
status = 200;
message = "Podcast has been updated";
} else if(podcast.getFeed() != null && podcast.getTitle()!=null){
podcastDao.createPodcast(podcast);
status = 201;
message = "The podcast you provided has been added to the database";
} else {
status = 406;
message = "The information you provided is not sufficient to perform either an UPDATE or "
+ " an INSERTION of the new podcast resource <br/>"
+ " If you want to UPDATE please make sure you provide an existent <strong>id</strong> <br/>"
+ " If you want to insert a new podcast please provide at least a <strong>title</strong> "
+ "and the <strong>feed</strong> for the podcast resource";
}
return Response.status(status).entity(message).build();
}
@DELETE
@Path("{id}")
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response deletePodcastById(@PathParam("id") Long id) {
if(podcastDao.deletePodcastById(id) == 1){
return Response.status(204).build();
} else {
return Response.status(404).entity("Podcast with the id " + id + " is not present in the database").build();
}
}
@DELETE
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response deletePodcasts() {
podcastDao.deletePodcasts();
return Response.status(200).entity("All podcasts have been successfully removed").build();
}
}
Notice the
@Path annotation’s value is a relative URI path. In the example above, the Java class will be hosted at the URI path
2.2.2.1. CREATE
For the creation of new resources(“podcasts”) I use the
POST (HTTP) method.
Note: In JAX-RS (Jersey) you specifies the HTTP methods (GET, POST, PUT, DELETE) by placing the corresponding annotation in front of the method.
2.2.2.1.1. Create a single resource (“podcast”) from JSON input
Annotations
2.2.2.1.2. Create multiple resources (“podcasts”) from JSON input
Annotations
In this case the method returns a status of 204 (“No Content”), suggesting that the server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation.
2.2.2.1.3. Create a single resource (“podcast”) from form
Annotations
– defines the media type, the method accepts, in this case
@FormParam – present before the input parameters of the method, this annotation binds the value(s) of a form parameter contained within a request entity body to a resource method parameter. Values
are URL decoded unless this is disabled using the
to the caller that the request has been fulfilled and resulted in a new resource being created.</code>
2.2.2.2. READ
2.2.2.2.1. Read a resources
I have created a single test class –
package com.npf.test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.junit.Assert;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.npf.model.Podcast;
public class RestDemoServiceIT {
@Test
public void testGetPodcast() throws Exception {
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(JacksonFeature.class);
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://localhost:8080/Restful-Jersey/podcasts/2");
Builder request = webTarget.request(MediaType.APPLICATION_JSON);
Response response = request.get();
Assert.assertTrue(response.getStatus() == 200);
Podcast podcast = response.readEntity(Podcast.class);
ObjectMapper mapper = new ObjectMapper();
System.out.print("Received podcast : "+ mapper.writerWithDefaultPrettyPrinter().writeValueAsString(podcast));
}
}
Note:
I had to register the JacksonFeature for the client too so that I can marshall the podcast response in JSON format – response.readEntity(Podcast.class)
I am testing against a running Tomcat on port 8080
I am expecting a 200 status for my request
With the help
ref link : http://www.codingpedia.org/ama/restful-web-services-example-in-java-with-jersey-spring-and-mybatis/
“architectural style consisting of a coordinated set of constraints applied to components, connectors, and data elements, within a distributed
hypermedia system” in Java, then you have come to the right place, because in this post I will present a simple RESTful API that maps REST calls to backend services offering
CRUD functionality.
1. The example
1.1. What does it do?
So, the best way to get to know the technology is build a prototype with it. And that’s exactly what I did and what I will present in this post. I’ve build a simple application that “manages” podcastsvia a REST API. It does CRUD operations on a single database table (Podcasts), triggered via the REST web services API. Though fairly simple, the example highlights the most common annotations you’ll need to build your own REST API.
1.2. Architecture and technologies
1.2.1. Jersey
The architecture is straightforward: with any REST client you can call the application’s API exposed viaJersey RESTful Web Services in JAVA. The Jersey RESTful Web Services framework is open source, production quality, framework for developing RESTful Web Services in Java that provides support for JAX-RS APIs and serves as a
JAX-RS (JSR 311 & JSR 339) Reference Implementation.
1.2.2. Spring
I like glueing stuff together withSpring, and this example is no exception. You’ll find out how Jersey 2 integrates with Spring.
1.2.3. Web Container
Everything gets packaged as a.warfile and can be deployed on any web container – I used
Tomcat .
1.2.4. Follow along
If you want to follow along, you find all you need on bitbucket:https://bitbucket.org/javarestfulwebservice/restful-jersey/src/f0a72f263d0281adf333bfcb71868ea69d931bb0?at=master
2. The coding
2.1. Configuration
2.1.1. Web Application Deployment Descriptor – web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>Restful-Jersey</display-name> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext.xml</param-value> </context-param> <servlet> <servlet-name>jersey-serlvet</servlet-name> <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class> <init-param> <param-name>javax.ws.rs.Application</param-name> <param-value>com.npf.init.MyResourceConfig</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>jersey-serlvet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> <context-param> <param-name>logbackConfigLocation</param-name> <param-value>classpath:logback.xml</param-value> </context-param> <listener> <listener-class>ch.qos.logback.ext.spring.web.LogbackConfigListener</listener-class> </listener> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
2.1.2.1. Jersey-servlet
Notice the Jersey servlet configuration [lines 18-33]. The
javax.ws.rs.core.Applicationclass defines the components of the JAX-RS application. Because I extended the
Application (ResourceConfig)class to provide the list of relevant root resource classes (
getResources()) and singletons (
getSingletons()), i.e. the JAX-RS application model, I needed to
register it in my web application
web.xmldeployment descriptor using a Servlet or Servlet filter initialization parameter with a name of
javax.ws.rs.Application.Check out the
documentation for other possibilities.
The implementation of
com.npf.init.MyResourceConfiglooks like the following in the project:
package com.npf.init;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.spring.scope.RequestContextFilter;
/**
*
* Registers the components to be used by the JAX-RS application
*
*
*/
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
/**
* which is a Spring filter that provides a bridge between JAX-RS and Spring request attributes
*/
register(RequestContextFilter.class);
/**
* which is a feature that registers Jackson JSON providers – you need it for the application to understand JSON data
*/
register(JacksonFeature.class);
/**
* scan the web service package
*/
packages("com.npf.web");
}
}
The class registers the following components
org.glassfish.jersey.server.spring.scope.RequestContextFilter, which is a Spring filter that provides a bridge between JAX-RS and Spring request attributespackage("com.npf.web"), which is the service component that exposes the REST API via annotations
org.glassfish.jersey.jackson.JacksonFeature, which is a feature that registers Jackson JSON providers – you need it for the application to understand JSON data
2.1.2.2. Spring application context configuration
The Spring application context configuration is located in the classpath under
spring/applicationContext.xml:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<import resource="classpath:spring/applicationContext-service.xml"/>
<import resource="classpath:spring/applicationContext-dao.xml"/>
<import resource="classpath:spring/applicationContext-aop.xml"/>
<context:component-scan base-package="com.npf"/>
</beans>
2.2. The RESTful API
2.2.1. Resources
As mentioned earlier, the demo application manages podcasts, which represent theresources in our web API. Resources are the central concept in REST and are characterized by two main things:
each is referenced with a global identifier (e.g. a
URI in HTTP).
has one or more representations, that they expose to the outer world and can be manipulated with (we’ll be working mostly with JSON representations in this example)
The podcast resources are represented in our application by the
Podcast class:
package com.npf.model;
import java.io.Serializable;
import java.util.Date;
import javax.xml.bind.annotation.XmlRootElement;
/**
* Podcast entity
*
*
*
* { "id":1, "title":"Quarks & Co - zum Mitnehmen-modified", "linkOnPodcastpedia":"http://www.podcastpedia.org/podcasts/1/Quarks-Co-zum-Mitnehmen", "feed":"http://podcast.wdr.de/quarks.xml", "description":"Quarks & Co: Das Wissenschaftsmagazin", "insertionDate":1388213547000 }
*
*/
@XmlRootElement
public class Podcast implements Serializable {
private static final long serialVersionUID = -8039686696076337053L;
/** id of the podcas */
private Long id;
/** title of the podcast */
private String title;
/** link of the podcast on Podcastpedia.org */
private String linkOnPodcastpedia;
/** url of the feed */
private String feed;
/** description of the podcast */
private String description;
/** when an episode was last published on the feed*/
private Date insertionDate;
public Podcast(){}
public Podcast(String title, String linkOnPodcastpedia, String feed,String description) {
this.title = title;
this.linkOnPodcastpedia = linkOnPodcastpedia;
this.feed = feed;
this.description = description;
}
public Podcast(Long id,String title, String linkOnPodcastpedia, String feed,String description) {
this.title = title;
this.linkOnPodcastpedia = linkOnPodcastpedia;
this.feed = feed;
this.description = description;
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getLinkOnPodcastpedia() {
return linkOnPodcastpedia;
}
public void setLinkOnPodcastpedia(String linkOnPodcastpedia) {
this.linkOnPodcastpedia = linkOnPodcastpedia;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getFeed() {
return feed;
}
public void setFeed(String feed) {
this.feed = feed;
}
public Date getInsertionDate() {
return insertionDate;
}
public void setInsertionDate(Date insertionDate) {
this.insertionDate = insertionDate;
}
}
The strucuture is pretty simple – there are an
id, which identifies a podcast, and several other fields that we’ll can see in the JSON representation:
{ "id":1, "title":"Quarks & Co - zum Mitnehmen-modified", "linkOnPodcastpedia":"http://www.podcastpedia.org/podcasts/1/Quarks-Co-zum-Mitnehmen", "feed":"http://podcast.wdr.de/quarks.xml", "description":"Quarks & Co: Das Wissenschaftsmagazin", "insertionDate":1388213547000 }
2.2.2. Methods
The API exposed by our example is described in the following table:Resource | URI | Method |
CREATE | ||
Add a list podcasts | /podcasts/list | POST |
Add a new podcast | /podcasts/ | POST |
READ | ||
List of all podcasts | /podcasts/ | GET |
List a single podcast | /podcasts/{id} | GET |
UPDATE | ||
Updates a single podcasts or creates one if not existent | /podcasts/{id} | PUT |
DELETE | ||
Delete all podcasts | /podcasts/ | DELETE |
Delete a single podcast | /podcasts/{id} | DELETE |
PodcastRestServiceclass is the one handling all the rest requests:
package com.npf.web;
import java.util.List;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.npf.dao.PodcastDao;
import com.npf.model.Podcast;
@Path("/podcasts")
public class PodcastRestService {
@Autowired
private PodcastDao podcastDao;
@POST
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_JSON})
@Transactional
public Response createPodcast(Podcast podcast) {
podcastDao.createPodcast(podcast);
return Response.status(201).entity("A new podcast/resource has been created").build();
}
@POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces({MediaType.TEXT_HTML}) @Transactional public Response createPodcastFromForm(@FormParam("title") String title, @FormParam("linkOnPodcastpedia") String linkOnPodcastpedia, @FormParam("feed") String feed, @FormParam("description") String description) { Podcast podcast = new Podcast(5L,title, linkOnPodcastpedia, feed, description); podcastDao.createPodcast(podcast); return Response.status(201).entity("A new podcast/resource has been created").build(); }
@POST @Path("list") @Consumes({MediaType.APPLICATION_JSON}) @Transactional public Response createPodcasts(List<Podcast> podcasts) { for(Podcast podcast : podcasts){ podcastDao.createPodcast(podcast); } return Response.status(204).build(); }
@GET @Path("{id}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response findById(@PathParam("id") Long id) { Podcast podcastById = podcastDao.getPodcastById(id); if(podcastById != null) { return Response.status(200).entity(podcastById).build(); } else { return Response.status(404).entity("The podcast with the id " + id + " does not exist").build(); } }
@PUT
@Path("{id}")
@Consumes({MediaType.APPLICATION_JSON})
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response updatePodcastById(@PathParam("id") Long id, Podcast podcast) {
if(podcast.getId() == null) podcast.setId(id);
String message;
int status;
if(podcastDao.updatePodcast(podcast) == 1){
status = 200;
message = "Podcast has been updated";
} else if(podcast.getFeed() != null && podcast.getTitle()!=null){
podcastDao.createPodcast(podcast);
status = 201;
message = "The podcast you provided has been added to the database";
} else {
status = 406;
message = "The information you provided is not sufficient to perform either an UPDATE or "
+ " an INSERTION of the new podcast resource <br/>"
+ " If you want to UPDATE please make sure you provide an existent <strong>id</strong> <br/>"
+ " If you want to insert a new podcast please provide at least a <strong>title</strong> "
+ "and the <strong>feed</strong> for the podcast resource";
}
return Response.status(status).entity(message).build();
}
@DELETE
@Path("{id}")
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response deletePodcastById(@PathParam("id") Long id) {
if(podcastDao.deletePodcastById(id) == 1){
return Response.status(204).build();
} else {
return Response.status(404).entity("Podcast with the id " + id + " is not present in the database").build();
}
}
@DELETE
@Produces({MediaType.TEXT_HTML})
@Transactional
public Response deletePodcasts() {
podcastDao.deletePodcasts();
return Response.status(200).entity("All podcasts have been successfully removed").build();
}
}
Notice the
@Path("/podcasts")before the class definition. The
@Path annotation’s value is a relative URI path. In the example above, the Java class will be hosted at the URI path
/podcasts. The
PodcastDaointerface is used to communicate with the database.
2.2.2.1. CREATE
For the creation of new resources(“podcasts”) I use the
POST (HTTP) method.
Note: In JAX-RS (Jersey) you specifies the HTTP methods (GET, POST, PUT, DELETE) by placing the corresponding annotation in front of the method.
2.2.2.1.1. Create a single resource (“podcast”) from JSON input
@POST @Consumes({MediaType.APPLICATION_JSON}) @Produces({MediaType.APPLICATION_JSON}) @Transactional public Response createPodcast(Podcast podcast) { podcastDao.createPodcast(podcast); return Response.status(201).entity("A new podcast/resource has been created").build(); }
Annotations
<code>@POST</code> – indicates that the method responds to HTTP POST requests
@Consumes({MediaType.APPLICATION_JSON})– defines the media type, the method accepts, in this case
"application/json"
@Produces({MediaType.TEXT_HTML})– defines the media type) that the method can produce, in this case
"text/html". The response will be a html document, with a status of 201, indicating to the caller that the request has been fulfilled and resulted in a new resource being created.
@Transactional– Spring annotation, specifies that the method execution, should take place inside a transaction
2.2.2.1.2. Create multiple resources (“podcasts”) from JSON input
@POST @Path("list") @Consumes({MediaType.APPLICATION_JSON}) @Transactional public Response createPodcasts(List<Podcast> podcasts) { for(Podcast podcast : podcasts){ podcastDao.createPodcast(podcast); } return Response.status(204).build(); }
Annotations
@POST– indicates that the method responds to HTTP POST requests
@Path("/list")– identifies the URI path that the class method will serve requests for. Paths are relative. The combined path here will be
"/podcasts/list", because as we have seen we have
@Pathannotation at the class level
@Consumes({MediaType.APPLICATION_JSON})– defines the media type, the method accepts, in this case
"application/json"
@Transactional– Spring annotation, specifies that the method execution, should take place inside a transaction
In this case the method returns a status of 204 (“No Content”), suggesting that the server has fulfilled the request but does not need to return an entity-body, and might want to return updated metainformation.
2.2.2.1.3. Create a single resource (“podcast”) from form
@POST @Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Produces({MediaType.TEXT_HTML}) @Transactional public Response createPodcastFromForm(@FormParam("title") String title, @FormParam("linkOnPodcastpedia") String linkOnPodcastpedia, @FormParam("feed") String feed, @FormParam("description") String description) { Podcast podcast = new Podcast(5L,title, linkOnPodcastpedia, feed, description); podcastDao.createPodcast(podcast); return Response.status(201).entity("A new podcast/resource has been created").build(); }
Annotations
@POST– indicates that the method responds to HTTP POST requests
@Consumes({MediaType.APPLICATION_FORM_URLENCODED})
– defines the media type, the method accepts, in this case
"application/x-www-form-urlencoded"
@FormParam – present before the input parameters of the method, this annotation binds the value(s) of a form parameter contained within a request entity body to a resource method parameter. Values
are URL decoded unless this is disabled using the
Encodedannotation
<code>@Produces({MediaType.TEXT_HTML})- defines the media type) that the method can produce, in this case "text/html". The response will be a html document, with a status of 201, indicating
to the caller that the request has been fulfilled and resulted in a new resource being created.</code>
@Transactional– Spring annotation, specifies that the method execution, should take place inside a transaction
2.2.2.2. READ
2.2.2.2.1. Read a resources
@GET @Path("{id}") @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML}) public Response findById(@PathParam("id") Long id) { Podcast podcastById = podcastDao.getPodcastById(id); if(podcastById != null) { return Response.status(200).entity(podcastById).build(); } else { return Response.status(404).entity("The podcast with the id " + id + " does not exist").build(); } }
3.1.2. Build the integration tests
I am using JUnit as the testing framework. By default, the Failsafe Plugin will automatically include all test classes with the following wildcard patterns:<tt>"**/IT*.java"</tt>– includes all of its subdirectories and all java filenames that start with “IT”.
<tt>"**/*IT.java"</tt>– includes all of its subdirectories and all java filenames that end with “IT”.
<tt>"**/*ITCase.java"</tt>– includes all of its subdirectories and all java filenames that end with “ITCase”.
I have created a single test class –
RestDemoServiceIT– that will test the read (GET) methods, but the procedure should be the same for all the other:
package com.npf.test;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Invocation.Builder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.junit.Assert;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.npf.model.Podcast;
public class RestDemoServiceIT {
@Test
public void testGetPodcast() throws Exception {
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(JacksonFeature.class);
Client client = ClientBuilder.newClient(clientConfig);
WebTarget webTarget = client.target("http://localhost:8080/Restful-Jersey/podcasts/2");
Builder request = webTarget.request(MediaType.APPLICATION_JSON);
Response response = request.get();
Assert.assertTrue(response.getStatus() == 200);
Podcast podcast = response.readEntity(Podcast.class);
ObjectMapper mapper = new ObjectMapper();
System.out.print("Received podcast : "+ mapper.writerWithDefaultPrettyPrinter().writeValueAsString(podcast));
}
}
Note:
I had to register the JacksonFeature for the client too so that I can marshall the podcast response in JSON format – response.readEntity(Podcast.class)
I am testing against a running Tomcat on port 8080
I am expecting a 200 status for my request
With the help
org.codehaus.jackson.map.ObjectMapperI am displaying the JSON response nicely formatted
3.1.3. Running the integration tests
4. Summary
source link : https://bitbucket.org/javarestfulwebservice/restful-jersey/src/f0a72f263d0281adf333bfcb71868ea69d931bb0?at=masterref link : http://www.codingpedia.org/ama/restful-web-services-example-in-java-with-jersey-spring-and-mybatis/
相关文章推荐
- java层打印调用栈两中方法
- Java web:Ubuntu下不用IDE创建Servlet
- java开发者工具下载地址集
- Java NIO 学习(五)--DataGramChannel
- Mac 使用Sublime Text 3搭建java环境
- webservice的简单demo(基于Java)
- Spring中BeanPostProcessor和BeanFactoryPostProcessor对比
- 代码审计之SpringMVC框架的安全攻防问题研究
- Java容器类总结
- Java并发编程:并发容器之CopyOnWriteArrayList(转载)
- JavaBean
- 理解java回调机制
- Struts2的拦截器interceptor
- SpringMvc自动任务调度之task实现项目源码
- JAVA8之Stream总结(给自己)
- 算法:有序表的二分查找
- Java IO流分析整理
- Java中实现文件上传下载的三种解决方案(推荐)
- spring mvc RedirectAttributes 的使用
- [Spring MVC] - @ModelAttribute使用