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"/>

相关文章
|
1天前
|
SQL 安全 数据库
Python Web开发者必学:SQL注入、XSS、CSRF攻击与防御实战演练!
【7月更文挑战第26天】在 Python Web 开发中, 安全性至关重要。本文聚焦 SQL 注入、XSS 和 CSRF 这三大安全威胁,提供实战防御策略。SQL 注入可通过参数化查询和 ORM 框架来防范;XSS 则需 HTML 转义用户输入与实施 CSP;CSRF 防御依赖 CSRF 令牌和双重提交 Cookie。掌握这些技巧,能有效加固 Web 应用的安全防线。安全是持续的过程,需贯穿开发始终。
6 1
Python Web开发者必学:SQL注入、XSS、CSRF攻击与防御实战演练!
|
4天前
|
前端开发 API 数据库
告别繁琐,拥抱简洁!Python RESTful API 设计实战,让 API 调用如丝般顺滑!
【7月更文挑战第23天】在Python的Flask框架下构建RESTful API,为在线商店管理商品、订单及用户信息。以商品管理为例,设计简洁API端点,如GET `/products`获取商品列表,POST `/products`添加商品,PUT和DELETE则分别用于更新和删除商品。使用SQLAlchemy ORM与SQLite数据库交互,确保数据一致性。实战中还应加入数据验证、错误处理和权限控制,使API既高效又安全,便于前端或其他服务无缝对接。
26 9
|
12天前
|
缓存 监控 Java
Java虚拟机(JVM)性能调优实战指南
在追求软件开发卓越的征途中,Java虚拟机(JVM)性能调优是一个不可或缺的环节。本文将通过具体的数据和案例,深入探讨JVM性能调优的理论基础与实践技巧,旨在为广大Java开发者提供一套系统化的性能优化方案。文章首先剖析了JVM内存管理机制的工作原理,然后通过对比分析不同垃圾收集器的适用场景及性能表现,为读者揭示了选择合适垃圾回收策略的数据支持。接下来,结合线程管理和JIT编译优化等高级话题,文章详细阐述了如何利用现代JVM提供的丰富工具进行问题诊断和性能监控。最后,通过实际案例分析,展示了性能调优过程中可能遇到的挑战及应对策略,确保读者能够将理论运用于实践,有效提升Java应用的性能。 【
54 10
|
12天前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
【7月更文挑战第15天】Python的协程与异步函数优化Web性能,通过非阻塞I/O提升并发处理能力。使用aiohttp库构建异步服务器,示例代码展示如何处理GET请求。异步处理减少资源消耗,提高响应速度和吞吐量,适用于高并发场景。掌握这项技术对提升Web应用性能至关重要。
37 10
|
14天前
|
前端开发 API 开发者
Python Web开发者必看!AJAX、Fetch API实战技巧,让前后端交互如丝般顺滑!
【7月更文挑战第13天】在Web开发中,AJAX和Fetch API是实现页面无刷新数据交换的关键。在Flask博客系统中,通过创建获取评论的GET路由,我们可以展示使用AJAX和Fetch API的前端实现。AJAX通过XMLHttpRequest发送请求,处理响应并在成功时更新DOM。Fetch API则使用Promise简化异步操作,代码更现代。这两个工具都能实现不刷新页面查看评论,Fetch API的语法更简洁,错误处理更直观。掌握这些技巧能提升Python Web项目的用户体验和开发效率。
26 7
|
10天前
|
前端开发 JavaScript UED
Python Web应用中的WebSocket实战:前后端分离时代的实时数据交换
【7月更文挑战第16天】在前后端分离的Web开发中,WebSocket解决了实时数据交换的问题。使用Python的Flask和Flask-SocketIO库,后端创建WebSocket服务,监听并广播消息。前端HTML通过JavaScript连接到服务器,发送并显示接收到的消息。WebSocket适用于实时通知、在线游戏等场景,提升应用的实时性和用户体验。通过实战案例,展示了如何实现这一功能。
|
5天前
|
存储 JSON API
实战派教程!Python Web开发中RESTful API的设计哲学与实现技巧,一网打尽!
【7月更文挑战第22天】构建RESTful API实战:**使用Python Flask设计图书管理API,遵循REST原则,通过GET/POST/PUT/DELETE操作处理/books及/books/&lt;id&gt;。示例代码展示资源定义、请求响应交互。关键点包括HTTP状态码的使用、版本控制、错误处理和文档化。本文深入探讨设计哲学与实现技巧,助力理解RESTful API开发。
16 0
|
6天前
|
缓存 中间件 网络架构
Python Web开发实战:高效利用路由与中间件提升应用性能
【7月更文挑战第20天】在Python Web开发中,路由与中间件是构建高效应用的核心。路由通过装饰器如`@app.route()`在Flask中映射请求至处理函数;中间件(如`@app.before_request`, `@app.after_request`)则在请求流程中插入自定义逻辑。优化路由包括减少冲突、利用动态参数及蓝图;中间件可用于缓存响应、请求验证和异常处理,显著提升性能和可维护性。良好设计是关键,示例代码展示了如何在Flask中实现这些策略。
20 0
|
16天前
|
算法 Java 开发者
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
Java面试题:Java内存探秘与多线程并发实战,Java内存模型及分区:理解Java堆、栈、方法区等内存区域的作用,垃圾收集机制:掌握常见的垃圾收集算法及其优缺点
18 0
|
16天前
|
安全 Java 调度
Java面试题:Java内存优化、多线程安全与并发框架实战,如何在Java应用中实现内存优化?在多线程环境下,如何保证数据的线程安全?使用Java并发工具包中的哪些工具可以帮助解决并发问题?
Java面试题:Java内存优化、多线程安全与并发框架实战,如何在Java应用中实现内存优化?在多线程环境下,如何保证数据的线程安全?使用Java并发工具包中的哪些工具可以帮助解决并发问题?
17 0