Java RESTful Web Service实战(第2版) 1.5 快速实现Java REST服务

1.5 快速实现Java REST服务

本节包含两个Jersey实战示例,目的是让读者具备快速创建REST服务的能力。

在开始实战之前,首先需要读者确认你的环境是否已经安装了Java和Maven。这里使用Maven命令,示例如下。

mvn -v

 

Apache Maven 3.3.3
(7994120775791599e205a5524ec3e0dfe41d4a06; 2015-04-22T19:57:37+08:00)

Maven home: /usr/local/Cellar/maven/3.3.3/libexec

Java version: 1.8.0_40, vendor: Oracle
Corporation

Java home:
/Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home/jre

Default locale: zh_CN, platform encoding:
UTF-8

OS name: "mac os x", version:
"10.11.1", arch: "x86_64", family: "mac"

从Maven版本显示命令的结果中,自上而下可以看到Maven的版本信息和HOME路径信息、Java的版本信息和HOME路径信息、本地语言、平台字符集以及操作系统信息。

1.5.1 第一个REST服务

Jersey提供了Maven原型(archetype)来快速创建REST服务项目。

1. 创建项目

我们首先使用archetypeGroupId为org.glassfish.jersey.archetypes的原型、archetypeArtifactId为jersey-quickstart-grizzly2的原型,创建REST服务项目。示例如下。

mvn archetype:generate \

-DarchetypeArtifactId=jersey-quickstart-grizzly2
\

-DarchetypeGroupId=org.glassfish.jersey.archetypes
\

-DinteractiveMode=false \

-DgroupId=my.restful \

-DartifactId=my-first-service \

-Dpackage=my.restful \

-DarchetypeVersion=2.22.1

上述命令将创建一个标准的Maven工程。其中,interactiveMode=false代表无需交互,archetypeVersion指定原型的版本,这个版本与Jersey的版本一致。groupId、artifactId和package分别定义了我们这个项目的组ID为my.restful,工件ID为my-first-service,包名为my.restful。我们通过观察项目根目录下的pom.xml,可以对应出上述命令参数与Maven坐标的关系。相关部分的示例如下。

<groupId>my.restful</groupId>

<artifactId>my-first-service</artifactId>

<packaging>jar</packaging>

<version>1.0-SNAPSHOT</version>

<name>my-first-service</name>

2. 运行服务

Maven工程建立好后,我们首先启动REST服务体验一下该项目的功能。进入项目的根目录,并执行如下命令构建和启动服务。

cd my-first-service

mvn package

mvn exec:java

 

Jersey app started with WADL available at
http://localhost:8080/myapp/application.wadl

Hit enter to stop it...

该命令启动了REST服务,端口是8080,我们可以随时通过回车键停止这个服务。同时,该服务还提供了WADL(详见1.6节)。通过访问application.wadl,可以获取当前REST服务公布的接口。本例WADL的关键部分,示例如下。

<ns0:resources
base="http://localhost:8080/myapp/">

   
<ns0:resource path="myresource">

       
<ns0:method id="getIt" name="GET">

            <ns0:response>

                <ns0:representation
mediaType="text/plain" />

           
</ns0:response>

       
</ns0:method>

   
</ns0:resource>

</ns0:resources>

这里定义了一个资源路径myresource,在该路径下,定义了一个GET方法getIt,表述类型为text/plain。

3. 访问服务

我们使用cURL(详见1.8节)来访问REST服务公布的myresource资源方法getIt,示例如下。

curl http://localhost:8080/myapp/myresource

 

Got it!

HTTPie(读作H-T-T-Pie)是和cURL类似的CLI工具,但交互上更人性化。我们使用HTTPie请求相同的资源地址,请求和响应信息如下。

http http://localhost:8080/myapp/myresource

 

HTTP/1.1 200 OK

Content-Length: 7

Content-Type: text/plain

Date: Sat, 14 Nov 2015 04:08:54 GMT

 

Got it!

响应信息的第一行包含了HTTP协议版本和状态码,接下来是部分HTTP HEAD信息,最后是HTTP BODY信息。cURL携带-i或者--include参数可以得到相同的结果,示例如下。

curl -i
http://localhost:8080/myapp/myresource

HTTP/1.1 200 OK

Content-Type: text/plain

Date: Sat, 14 Nov 2015 04:08:54 GMT

Content-Length: 7

 

Got it!

要想获得更多的cURL请求响应信息,可以使用-v参数,示例如下。

curl -v
http://localhost:8080/myapp/myresource

 

* Hostname was NOT found in DNS cache

*  
Trying ::1...

* connect to ::1 port 8080 failed:
Connection refused

*  
Trying fe80::1...

* connect to fe80::1 port 8080 failed:
Connection refused

*  
Trying 127.0.0.1...

* Connected to localhost (127.0.0.1) port
8080 (#0)

> GET /myapp/myresource HTTP/1.1

> User-Agent: curl/7.38.0

> Host: localhost:8080

> Accept: */*

< HTTP/1.1 200 OK

< Content-Type: text/plain

< Date: Sat, 14 Nov 2015 04:08:56 GMT

< Content-Length: 7

* Connection #0 to host localhost left
intact

Got it!

4. 分析项目

完成了最初的体验后,我们来分析下面这个示例工程。首先,从启动服务的命令mvn exec:java入手。该命令实际调用了exec-maven-plugin插件中定义的一个值为java的goal,用以触发mainClass中的main函数。本例的mainClass定义为my.restful.Main。在pom.xml中,exec插件完整定义如下。

<plugin>

   
<groupId>org.codehaus.mojo</groupId>

   
<artifactId>exec-maven-plugin</artifactId>

   
<version>1.2.1</version>

   
<executions>

       
<execution>

           
<goals>

                <goal>java</goal>

           
</goals>

       
</execution>

   
</executions>

   
<configuration>

       
<mainClass>my.restful.Main</mainClass>

   
</configuration>

</plugin>

除了pom.xml和Main类,示例还包含哪些内容呢?我们可以使用如下命令查看。

tree .

.

├── pom.xml

└── src

    ├── main

    │   └── java

    │       └── my

    │           └── restful

    │               ├── Main.java

    │               └── MyResource.java

    └── test

       
└── java

           
└── my

                └── restful

                    └──
MyResourceTest.java

源代码中,还包括了资源类MyResource和它的单元测试类MyResourceTest。

在资源类MyResource中,@Path中定义了资源路径,@GET中定义了GET方法getIt(),@Produces中定义了响应的类型为普通的字符串,示例如下。

@Path("myresource")

public class MyResource {

   
@GET

   
@Produces(MediaType.TEXT_PLAIN)

   
public String getIt() {

       
return "Got it!";

    }

}

相应地,资源测试类MyResourceTest中实现了对getIt()方法的测试,示例如下。

@Test

public void testGetIt() {

   
String responseMsg =
target.path("myresource").request().get(String.class);

   
assertEquals("Got it!", responseMsg);

}

在上述代码中,target()、path()、request()和get()方法都是Jersey Client中定义的方法,这些方法组合在一起,形成了流式风格的API。我们期待响应值为“Got it!”的字符串。

5. 单元测试

最后,我们使用如下命令执行单元测试。使用IDE可以直接通过图形界面单击对该方法的测试。

mvn test

 

-------------------------------------------------------

 T E
S T S

-------------------------------------------------------

Running my.restful.MyResourceTest

Results:

 

Tests run: 1, Failures: 0, Errors: 0,
Skipped: 0

jersey-quickstart-grizzly2原型提供的模板代码,使用了main函数,并在其中启动了Grizzly的HttpServer。这是典型的Java SE形式的REST应用。更多情况下,我们希望得到的是一个可以以war包形式部署到Servlet容器的轻量级Java EE项目。接下来的示例就是这样的Web形式的项目。

1.5.2 第一个Servlet容器服务

jersey-quickstart-webapp原型会为我们生成Servlet容器服务。

1. 创建项目

使用如下命令创建名为my-first-webapp的Web项目。

mvn archetype:generate \

-DarchetypeArtifactId=jersey-quickstart-webapp
\

-DarchetypeGroupId=org.glassfish.jersey.archetypes
\

-DinteractiveMode=false \

-DgroupId=my.restful \

-DartifactId=my-first-webapp \

-Dpackage=my.restful \

-DarchetypeVersion=2.22.1

2. 运行服务

由于这是一个Web项目,没有main函数,我们必须将其部署到Servlet容器(比如Tomcat、Jetty)中,才能将其运行。在开发阶段,我们无需真正将其部署,而是使用Maven插件这种更轻量级的方式启动服务。在pom.xml中,增加如下定义来添加插件。

<plugin>

  
<groupId>org.eclipse.jetty</groupId>

  
<artifactId>jetty-maven-plugin</artifactId>

  
<version>9.3.5.v20151012</version>

</plugin>

有了插件,我们可以使用如下命令编译和启动服务,使用Ctrl+C停止服务。

mvn jetty:run

如果我们要对示例项目进行断点调试,应在服务启动前设置监听端口等信息。这里以IntelliJ IDEA所使用的5050端口为例,示例如下。

export MAVEN_OPTS="-Xdebug -Xnoagent
-Djava.compiler=NONE -Xrunjdwp: transport=dt_socket,address=5050,server=y,suspend=y"

mvn jetty:run

以这样的方式启动服务,需要IDE与之交互。过程是首先启动端口,然后IDE向该端口请求监听,服务启动并接收请求,在代码的某个断点处,服务会向该端口推送事件,IDE在代码的断点处停留并高亮显示该行。

3. 访问服务

服务启动后,我们使用HTTPie请求资源地址,示例如下。

http
http://localhost:8080/webapi/myresource

 

HTTP/1.1 200 OK

Content-Length: 7

Content-Type: text/plain

Date: Sat, 14 Nov 2015 08:00:03 GMT

Server: Jetty(9.3.5.v20151012)

 

Got it!

4. 分析项目

本例是一个标准的Maven Web工程。Web的根目录默认名称为webapp,默认的Servlet版本为2.5,需要使用WEB-INF/web.xml文件来配置REST服务。我们通过tree命令得到完整的工程结构如下。

tree .

.

├── my-first-webapp.iml

├── pom.xml

└── src

    └── main

       
├── java

       
│   └── my

       
│       └── restful

       
│           └──
MyResource.java

       
├── resources

       
└── webapp

           
├── WEB-INF

           
│   └── web.xml

          
 └── index.jsp

5. 扩展项目

本例与前例提供的资源类和资源方法相同,我们在此基础上增加两个资源方法,分别用来新增和查询资源,示例如下。

private static ConcurrentHashMap<String,
MyDomain> map=new ConcurrentHashMap<>();

 

@GET

@Path("{key}")

@Produces(MediaType.APPLICATION_XML)

public MyDomain
getMy(@PathParam("key") final String key) {

   
final MyDomain myDomain = map.get(key);

   
if (myDomain == null) {

       
return new MyDomain();

    }

   
return myDomain;

}

 

@POST

@Consumes(MediaType.APPLICATION_XML)

public void addMy(final MyDomain myDomain)
{

   
map.put(myDomain.getName(), myDomain);

}

如上所示,POST方法addMy用于接收并存储新增的表述,GET方法getMy用于查询表述。MyDomain类是基于JAXB的POJO类,用于表示XML格式的表述。

首先,我们通过如下命令,新增一条记录。

curl -X POST
http://localhost:8080/webapi/myresource -d '<myDomain name="eric"
value="feuyeux@gmail.com"/>' -H
"Content-type:application/xml"

然后通过如下命令查询和验证新增记录的存在。

curl
http://localhost:8080/webapi/myresource/eric

 

<?xml version="1.0"
encoding="UTF-8" standalone="yes"?><myDomain
name="eric" value="feuyeux@gmail.com"/>

时间: 2025-01-02 12:54:11

Java RESTful Web Service实战(第2版) 1.5 快速实现Java REST服务的相关文章

Java RESTful Web Service实战(第2版)

Java核心技术系列 Java RESTful Web Service实战 (第2版) 韩陆 著 图书在版编目(CIP)数据 Java RESTful Web Service实战 / 韩陆著. -2版. -北京:机械工业出版社,2016.7 (Java核心技术系列) ISBN 978-7-111-54213-1 Ⅰ. J-   Ⅱ. 韩-   Ⅲ. JAVA语言-程序设计   Ⅳ. TP312 中国版本图书馆CIP数据核字(2016)第156331号 Java RESTful Web Servi

Java RESTful Web Service实战(第2版) 导读

Java核心技术系列 Java RESTful Web Service实战 (第2版) 韩陆 著   半年前初识韩陆的时候,我们就聊到他正在写的这本书,当得知我从2006年就参与了Apache CXF开发,他立即邀请我为他的新书写序,我也就欣然答应了. Apache CXF作为JAXWS以及JAX-RS规范的实现框架,已经成为很多Web服务开发者必选的开发框架.作为这一框架的开发维护者之一,我的日常工作经常需要熟悉这些JSR规范,并实现JSR所定义的API,解决最终用户的使用问题. 熟悉Java

Java RESTful Web Service实战(第2版) 2.3 传输格式

2.3 传输格式 本节要考虑的就是如何设计表述,即传输过程中数据采用什么样的数据格式.通常,REST接口会以XML和JSON作为主要的传输格式,这两种格式数据的处理是本节的重点.那么Jersey是否还支持其他的数据格式呢?答案是肯定的,让我们逐一掌握各种类型的实现. 2.3.1 基本类型 Java的基本类型又叫原生类型,包括4种整型(byte.short.int.long).2种浮点类型(float.double).Unicode编码的字符(char)和布尔类型(boolean). 阅读指南 本

Java RESTful Web Service实战(第2版) 1.2 解读REST服务

1.2 解读REST服务 RESTful对应的中文是REST式的,RESTful Web Service的准确翻译应该是REST式的Web服务,我们通常简称为REST服务.RESTful的应用或者Web服务是最常见的两种REST式的项目部署.存在的方式.本节将介绍REST服务并对比其与传统Web Services的不同. 1.2.1 REST式的Web服务 RESTful Web Service是一种遵守REST式风格的Web服务.REST服务是一种ROA(Resource-Oriented A

Java RESTful Web Service实战(第2版) 1.3 解读JAX-RS标准

1.3 解读JAX-RS标准 JAX-RS是Java领域的REST式的Web服务的标准规范,是使用Java完成REST服务的基本约定. 1.3.1 JAX-RS2标准 Java领域中的Web Service是指实现SOAP协议的JAX-WS.直到Java EE 6(发布于2008年9月)通过JCP(Java Community Process)组织定义的JSR311(http://www.jcp.org/en/jsr/detail?id=311),才将REST在Java领域标准化. JSR311

Java RESTful Web Service实战(第2版) 1.4 Jersey项目概要

1.4 Jersey项目概要 Jersey是JAX-RS标准的参考实现,是Java领域中最纯正的REST服务开发框架.本节将带读者走近Jersey的世界. Jersey项目是GlashFish项目的一个子项目,专门用来实现JAX-RS(JSR 311 & JSR 339)标准,并提供了扩展特性. 1.4.1 获得Jersey Jersey项目的地址是https://jersey.java.net.该网站同时提供了JAX-RS和JAX-RS2两个并行版本,分别是JAX-RS1.1(截至本书发稿,最

Java RESTful Web Service实战(第2版) 1.6 快速了解Java REST服务

1.6 快速了解Java REST服务 1.6.1 REST工程类型 在REST服务中,资源类是接收REST请求并完成响应的核心类,而资源类是由REST服务的"提供者"来调度的.这一概念类似其他框架中自定义的Servlet类,该类会将请求分派给指定的Controller/Action类来处理.本节将讲述REST中的这个提供者,即JAX-RS2中定义的Application以及Servlet. Application类在JAX-RS2(JSR339,详见参考资料)标准中定义为javax.

Java RESTful Web Service实战(第2版) 1.7 Java领域的其他REST实现

1.7 Java领域的其他REST实现 Java领域存在很多REST实现,我们以是否遵循JAX-RS标准,将它们分为两组.前者是JAX-RS标准参考实现之外的厂商实现,后者要么是因为出现较JAX-RS标准早,要么干脆跳出了JAX-RS标准的定义,以自身框架一致性为目标,实现了一套独有的对REST开发的支持.本节将概括性地介绍这些实现工具,以便读者有所对比和选择. 1.7.1 JAX-RS的其他实现 JAX-RS标准发布后,诸多厂商推出了自己的基于JAX-RS标准的实现.其中最有影响力的当属来自J

Java RESTful Web Service实战(第2版) 1.9 本章小结

1.9 本章小结 本章主要讲述了REST服务的概念和实战.先后解读了REST.REST服务.JAX-RS2标准中的重要概念,对JAX-RS2的参考实现项目Jersey进行了简单而全面的概述.随后讲述了如何快速创建REST应用和REST服务,介绍了基于JAX-RS2标准的其他项目和其他非JAX-RS2标准的.著名的Java项目. 通过阅读本章,读者可以清楚地掌握Java领域开发REST服务中的基本概念. 本章主要的知识点如下. REST是什么 一种架构风格. HTTP+URI+XML是REST的基