使用 Jersey 和 Apache Tomcat 构建 RESTful Web 服务

RESTful Web 服务与 Jersey 实践
本文介绍 RESTful Web 服务的基本概念及其在 Java 中的实现方式,重点讲解了 Jersey 框架如何帮助开发者构建 RESTful Web 服务。文章通过一系列示例详细阐述了如何使用 Jersey 处理 HTTP 请求、实现资源管理和多种 MIME 类型的支持。

RESTful Web 服务简单介绍

REST 在 2000 年由 Roy Fielding 在博士论文中提出,他是 HTTP 规范 1.0 和 1.1 版的首席作者之中的一个。

REST 中最重要的概念是资源(resources),使用全球 ID(通常使用 URI)标识。

client应用程序使用 HTTP 方法(GET/ POST/ PUT/ DELETE)操作资源或资源集。RESTful Web 服务是使用 HTTP 和 REST 原理实现的 Web 服务。通常,RESTful Web 服务应该定义下面方面:

  • Web 服务的基/根 URI,比方 http://host/<appcontext>/resources。
  • 支持 MIME 类型的响应数据。包含 JSON/XML/ATOM 等等。

  • 服务支持的操作集合(比如 POST、GET、PUTDELETE)。

表 1 演示了典型 RESTful Web 服务中使用的资源 URI 和 HTTP 方法。

表 1. RESTful Web 服务演示样例
方法/资源资源集合。 URI 如:
http://host/<appctx>/resources
成员资源,URI 如:
http://host/<appctx>/resources/1234
GET列出资源集合的全部成员。

检索标识为 1234 的资源的表示形式。
PUT使用一个集合更新(替换)还有一个集合。更新标记为 1234 的数字资源。
POST在集合中创建数字资源,其 ID 是自己主动分配的。

在以下创建一个子资源。
DELETE删除整个资源集合。删除标记为 1234 的数字资源。

JSR 311 (JAX-RS) 和 Jersey

JSR 311 或 JAX-RS(用于 RESTful Web Services 的 Java API)的提议開始于 2007 年,1.0 版本号到 2008 年 10 月定稿。眼下。JSR 311 版本号 1.1 还处于草案阶段。该 JSR 的目的是提供一组 API 以简化 REST 样式的 Web 服务的开发。

在 JAX-RS 规范之前,已经有 Restlet 和 RestEasy 之类的框架,能够帮助您实现 RESTful Web 服务。可是它们不够直观。Jersey 是 JAX-RS 的參考实现。它包括三个主要部分。

  • 核心server(Core Server):通过提供 JSR 311 中标准化的凝视和 API 标准化,您能够用直观的方式开发 RESTful Web 服务。

  • 核心client(Core Client):Jersey client API 帮助您与 REST 服务轻松通信。
  • 集成(Integration):Jersey 还提供能够轻松集成 Spring、Guice、Apache Abdera 的库。

在本文的下面部分,我介绍了全部这些组件,可是更关注核心server。

构建 RESTful Web 服务

我将从能够集成到 Tomcat 的 “hello world” 应用程序開始。

该应用程序将带领您完毕设置环境的过程,并涉及 Jersey 和 JAX-RS 的基础知识。

然后,我将介绍更加复杂的应用程序,深入探讨 JAX-RS 的本质和特性,比方多个 MIME 类型表示形式支持、JAXB 支持等。我将从例子中摘取一些代码片段来介绍重要的概念。

Hello World:第一个 Jersey Web 项目

要设置开发环境。您须要下面内容:

  • IDE:Eclipse IDE for JEE (v3.4+) 或 IBM Rational Application Developer 7.5
  • Java SE5 或更高版本号
  • Web 容器:Apache Tomcat 6.0(Jetty 和其它也能够)
  • Jersey 库:Jersey 1.0.3 归档,包括全部必需的库

设置 Jersey 的环境

首先,为 Eclipse 上的 Tomcat 6.0 创建server执行时。

这是用于 RESTful Web 应用程序的 Web 容器。

然后创建一个名为 “Jersey” 应用程序,并将目标执行时指定为 Tomcat 6.0。

最后。从 Jersey 开发包中将下面库拷贝到 WEB-INF 下的库文件夹:

  • 核心服务器:jersey-core.jar。jersey-server.jar,jsr311-api.jar。asm.jar
  • 核心客户端:(用于測试)jersey-client.jar
  • JAXB 支持:(在高级例子中使用)jaxb-impl.jar,jaxb-api.jar。activation.jar,stax-api.jar。wstx-asl.jar
  • JSON 支持:(在高级例子中使用)jersey-json.jar

开发 REST 服务

如今。您已经设置好了开发第一个 REST 服务的环境。该服务对client发出 “Hello”。

要做到这一点,您须要将全部的 REST 请求发送到 Jersey 容器 —— 在应用程序的 web.xml 文件里定义 servlet 调度程序(參见清单 1)。除了声明 Jersey servlet 外,它还定义一个初始化參数,指示包括资源的 Java 包。

清单 1. 在 web.xml 文件里定义 Jersey servlet 调度程度

<servlet>
  <servlet-name>Jersey REST Service</servlet-name>
<servlet-class>
  com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
  <init-param>
    <param-name>com.sun.jersey.config.property.packages</param-name>
    <param-value>sample.hello.resources</param-value>
  </init-param>
  <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
  <servlet-name>Jersey REST Service</servlet-name>
  <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

如今您将编写一个名为 HelloResource 的资源,它接受 HTTP GET 并响应 “Hello Jersey”。

清单 2. sample.hello.resources 包中的 HelloResource

@Path("/hello")
public class HelloResource {
	@GET
	@Produces(MediaType.TEXT_PLAIN)
	public String sayHello() {
		return "Hello Jersey";
	}
}

该代码中有几个地方须要强调:

  • 资源类(Resource Class):注意,资源类是一个简单的 Java 对象 (POJO)。能够实现不论什么接口。这添加了很多优点,比方可重用性和简单。
  • 凝视(Annotation):在 javax.ws.rs.* 中定义,是 JAX-RS (JSR 311) 规范的一部分。
  • @Path:定义资源基 URI。由上下文根和主机名组成,资源标识符类似于 http://localhost:8080/Jersey/rest/hello。
  • @GET:这意味着下面方法能够响应 HTTP GET 方法。

  • @Produces:以纯文本方式定义响应内容 MIME 类型。

測试 Hello 应用程序

要測试应用程序,能够打开您的浏览器并输入 URL http://<host>:<port>/<appctx>/rest/hello。您将看到响应 “Hello Jersey”。这很easy,使用凝视处理请求、响应和方法。

下面部分将涉及 JAX-RS 规范的必要部分。使用 Contacts 演示例子应用程序中的代码片段进行介绍。

您能够在源码包中找到这个高级例子的全部代码

资源

资源是组成 RESTful Web 服务的关键部分。

您能够使用 HTTP 方法(如 GET、POST、PUTDELETE)操作资源。

应用程序中的全部内容都是资源:员工、联系人、组织等。

在 JAX-RX 中,资源通过 POJO 实现。使用@Path 凝视组成其标识符。资源能够有子资源。

在这样的情况下。父资源是资源集合,子资源是成员资源。

在例子 Contacts 应用程序中,您将操作个人联系人和联系人集合。

ContactsResource 是 /contacts URI 组成的集合资源。ContactResource 是 /contacts/{contactId} URI 组成的成员资源。下划线 JavaBean 是一个简单的 Contact 类,使用 id、名称和地址作为成员字段。

參见清单 3 和清单 4 了解详情。

清单 3. ContactsResource

@Path("/contacts")
public class ContactsResource {
	@Context
	UriInfo uriInfo;
	@Context
	Request request;

	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public List<Contact> getContacts() {
		List<Contact> contacts = >new ArrayList<Contact>();
		contacts.addAll( ContactStore.getStore().values() );
		return contacts;
	}

@Path("{contact}")
	public ContactResource getContact(
			@PathParam("contact") String contact) {
		return new ContactResource(uriInfo, request, contact);
	}
}

有几个有趣的地方须要注意。

  • @Context: 使用该凝视注入上下文对象,比方 Request、Response、UriInfo、ServletContext 等。
  • @Path("{contact}"):这是 @Path 凝视。与根路径 “/contacts” 结合形成子资源的 URI。
  • @PathParam("contact"):该凝视将參数注入方法參数的路径,在本例中就是联系人 id。其它可用的凝视有 @FormParam@QueryParam 等。
  • @Produces:响应支持多个 MIME 类型。在本例和上一个演示样例中。APPLICATION/XML 将是默认的 MIME 类型。

您或许还注意到了。GET 方法返回定制 Java 对象而不是 String(纯文本)。正如上一个 Hello World 演示样例所看到的。

JAX-RS 规范要求实现支持多个表示形式类型。比方 InputStream、byte[]、JAXB 元素、JAXB 元素集合等等,以及将其序列化为 XML、JSON 或纯文本作为响应的能力。下文我将提供很多其它有关表示形式技术的信息,尤其是 JAXB 元素表示形式。

清单 4. ContactResource

public class ContactResource {
	@Context
	UriInfo uriInfo;
	@Context
	Request request;
	String contact;
	
	public ContactResource(UriInfo uriInfo, Request request, 
			String contact) {
		this.uriInfo = uriInfo;
		this.request = request;
		this.contact = contact;
	}
	
	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Contact getContact() {
		Contact cont = ContactStore.getStore().get(contact);
		if(cont==null)
			throw new NotFoundException("No such Contact.");
		return cont;
	}
}

ContactResource 的代码简单明了。注意下面内容:

  • Representation Type Contact:Contact 是一个简单的 JavaBean,由 @XmlRootElement 凝视,这使它能够表示为 XML 或 JSON。
  • ContactStore:这是基于 HashMap 的内存数据存储库,事实上现对于本文不重要。

方法

HTTP 方法映射到资源的 CRUD(创建、读取、更新和删除) 操作。虽然您能够做一些小改动,比方让 PUT 方法变成创建或更新,但主要的模式例如以下:

  • HTTP GET:获取/列出/检索单个资源或资源集合。
  • HTTP POST:新建资源。
  • HTTP PUT:更新现有资源或资源集合。
  • HTTP DELETE:删除资源或资源集合。

由于我已经介绍过 GET 方法。我将从 POST 開始说明。就像其它方法一样。我仍然使用 Contact 演示样例进行说明。

POST

通常通过填写表单创建新联系人。也就是说。HTML 表单将 POST 到server,server创建并维护新创建的联系人。

清单 5 演示了该操作的server端逻辑。

清单 5. 接受表单提交(POST)并新建一个联系人

@POST
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void newContact(
		@FormParam("id") String id,
		@FormParam("name") String name,
		@Context HttpServletResponse servletResponse
) throws IOException {
	Contact c = new Contact(id,name,new ArrayList<Address>());
	ContactStore.getStore().put(id, c);
		
	URI uri = uriInfo.getAbsolutePathBuilder().path(id).build();
	Response.created(uri).build();
		
	servletResponse.sendRedirect("../pages/new_contact.html");
}

注意该演示样例的下面部分:

  • @Consumes:声明该方法使用 HTML FORM。

  • @FormParam:注入该方法的 HTML 属性确定的表单输入。
  • @Response.created(uri).build(): 构建新的 URI 用于新创建的联系人(/contacts/{id})并设置响应代码(201/created)。您能够使用 http://localhost:8080/Jersey/rest/contacts/<id> 訪问新联系人。

PUT

我使用 PUT 方法更新现有资源。可是,也能够通过更新实现。或者像清单 6 中的代码片段展示的那样创建一个资源。

清单 6. 接受 PUT 请求并创建或更新联系人

@PUT
@Consumes(MediaType.APPLICATION_XML)
public Response putContact(JAXBElement<Contact> jaxbContact) {
	Contact c = jaxbContact.getValue();
	return putAndGetResponse(c);
}

private Response putAndGetResponse(Contact c) {
	Response res;
	if(ContactStore.getStore().containsKey(c.getId())) {
		res = Response.noContent().build();
	} else {
		res = Response.created(uriInfo.getAbsolutePath()).build();
	}
	ContactStore.getStore().put(c.getId(), c);
	return res;
}

我还在本演示样例中包括了很多不同的概念,重点强调下面概念:

  • Consume XML:putContact() 方法接受 APPLICATION/XML 请求类型,而这样的输入 XML 将使用 JAXB 绑定到 Contact 对象。

    您将在下一节中找到client代码。

  • 空响应带有不同的状态码:PUT 请求的响应没有不论什么内容,可是有不同的状态码。假设数据存储库中存在联系人,我将更新该联系人并返回 204/no content。假设没有新联系人。我将创建一个并返回 201/created

DELETE

实现 DELETE 方法很easy。

演示样例请查看清单 7。

清单 7. 删除其 ID 确定的联系人

@DELETE
public void deleteContact() {
	Contact c = ContactStore.getStore().remove(contact);
	if(c==null)
		throw new NotFoundException("No such Contact.");
}

表示形式

在上一节中,我介绍了几个表示形式类型。

如今我将简要浏览一遍并深入探讨 JAXB 表示形式。其它受支持的表示形式有 byte[]、InputStream、File 等。

  • String:纯文本。

  • Response:一般 HTTP 响应,包括带有不同响应代码的定制内容。
  • Void:带有 204/no content 状态码的空响应。
  • Resource Class:将流程托付给该资源类。
  • POJO:使用 @XmlRootElement 凝视的 JavaBean。这让它成为一个 JAXB bean。能够绑定到 XML。
  • POJO 集合:JAXB bean 集合。

JAX-RS 支持使用 JAXB (Java API for XML Binding) 将 JavaBean 绑定到 XML 或 JSON。反之亦然。JavaBean 必须使用@XmlRootElement 凝视。清单 8 使用 Contact bean 作为演示样例。

没有明白 @XmlElement 凝视的字段将包括一个名称与之同样的 XML 元素。清单 9 显示了用于一个 Contact bean 的序列化 XML 和 JSON 表示形式。

联系人集合的表示形式与此同样,默认使用 <Contacts> 作为包装器元素。

清单 8. Contact bean

@XmlRootElement
public class Contact {
	private String id;
	private String name;
	private List<Address> addresses;
	
	public Contact() {}
	
	public Contact(String id, String name, List<Address> addresses) {
		this.id = id;
		this.name = name;
		this.addresses = addresses;
	}

	@XmlElement(name="address")
	public List<Address> getAddresses() {
		return addresses;
	}

	public void setAddresses(List<Address> addresses) {
		this.addresses = addresses;
	}
	// Omit other getters and setters
}
清单 9. 一个 Contact 的表示形式

XML representation:
<contact>
  <address>
    <city>Shanghai</city>
    <street>Long Hua Street</street>
  </address>
  <address>
    <city>Shanghai</city>
    <street>Dong Quan Street</street>
  </address>
  <id>huangyim</id>
    <name>Huang Yi Ming</name>
</contact>


JSON representation:
{"contact":[{"address":[{"city":"Shanghai","street":"Long
            Hua Street"},{"city":"Shanghai","street":"Dong Quan
            Street"}],"id":"huangyim","name":"Huang Yi Ming"}]}

与 REST 服务通讯的client

在眼下为止的演示样例中,我开发了一个支持 CRUD 的 RESTful Web 服务。

如今我開始解释怎样使用 curl 和 Jersey client API 与该 REST 服务通讯。这样一来,我能够測试server端代码,并介绍很多其它有关client技术的信息。

使用 curl 与 REST 服务通讯

Curl 是一个流行的命令行工具,能够向使用 HTTP 和 HTTPS 协议的server发送请求。这是一个与 RESTful Web 服务通讯的好工具。由于它能够通过不论什么 HTTP 方法发送内容。

Curl 已经在 Linux 和 Mac 中自带了。而且有一个有用工具。能够在 Windows® 平台上进行安装。

如今,我们初始化获取全部联系人的第一个 curl 命令。您能够參考 清单 3 获取server端代码。

curl http://localhost:8080/Jersey/rest/contacts

响应将使用 XML 并包括全部联系人。

注意,getContacts() 方法还生成一个 application/json MIME 类型响应。

您还能够请求该类型的内容。

curl –HAccept:application/json http://localhost:8080/Jersey/rest/contacts

响应将是一个包括全部联系人的 JSON 字符串。

如今,我将 PUT 一个新的联系人。注意。清单 6 中的putContact() 方法接受 XML 并使用 JAXB 将 XML 绑定到 Contact 对象。

curl -X PUT -HContent-type:application/xml --data "<contact><id>foo</id>
                <name>bar</name></contact>" http://localhost:8080/Jersey/rest/contacts/foo

一个通过 “foo” 识别的新联系人将加入到联系人存储库。

您能够使用 URI /contacts 或 /contacts/foo 验证联系人集合或单个联系人。

使用 Jersey Client 与 REST 服务通讯

Jersey 还提供了一个client库,帮助您与server通讯并对 RESTful 服务进行单元測试。该库是一个一般实现,能够整合不论什么 HTTP/HTTPS-based Web 服务。

client的核心类是 WebResource 类。您能够使用该类依据根 URI 构建一个请求 URL,然后发送请求并获取响应。清单 10 展示了怎样创建WebResource 实例。

注意 WebResource 是一个大对象,因此仅仅创建一次。

清单 10. 创建 WebResource 实例

Client c = Client.create();
WebResource r=c.resource("http://localhost:8080/Jersey/rest/contacts");

第一个 Jersey client演示样例将发送 GET 请求获取全部联系人并打印响应状态码和响应内容,參见清单 11。

清单 11. GET 全部联系人并打印响应

ClientResponse response = r.get(ClientResponse.class);
System.out.println( response.getStatus() );
System.out.println( response.getHeaders().get("Content-Type") );
String entity = response.getEntity(String.class);
System.out.println(entity);

清单 12 展示了还有一个创建通过 “foo” 识别的新联系人的演示样例。

清单 12. 创建一个联系人

Address[] addrs = {
	new Address("Shanghai", "Ke Yuan Street")
};
Contact c = new Contact("foo", "Foo Bar", Arrays.asList(addrs));

ClientResponse response = r
	.path(c.getId())
	.accept(MediaType.APPLICATION_XML)
	.put(ClientResponse.class, c);
System.out.println(response.getStatus());

注意 WebResource 实例的 API。它构建 URI。设置请求头,并在一行代码中调用请求。内容(Contact 对象)将自己主动绑定到 XML。

清单 13 展示了检索通过 “foo” 识别的联系人(已上一个演示样例中创建)的最后一个演示样例然后删除该联系人。

清单 13. 检索 “foo” 联系人并删除

GenericType<JAXBElement<Contact>> generic = new GenericType<JAXBElement<Contact>>() {};
JAXBElement<Contact> jaxbContact = r
	.path("foo")
	.type(MediaType.APPLICATION_XML)
	.get(generic);
Contact contact = jaxbContact.getValue();
System.out.println(contact.getId() + ": " + contact.getName());

ClientResponse response = r.path("foo").delete(ClientResponse.class);
System.out.println(response.getStatus());

注意。当您想获取 JAXB bean 响应时,您须要使用 Java 2 Platform, Standard Edition (J2SE) 中引入的范型特性。

使用 Jersey client练习这些演示例子。

您能够在资源包中找到很多其它例子代码。

还能够參考 Jersey 站点查看很多其它信息。

结束语

Jersey 能够使用 Jersey 集成库与其它框架或有用工具库集成。

眼下,Jersey 能够集成 Spring、Guice,还支持 ATOM 表示形式与 apache-adbera 的集成。

在 Jersey 项目主页能够找到 API 和入门指南。

下载

描写叙述名字大小
源码Jersey.Sample.Contact.Src.zip

10KB

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值