您的位置:首页 > 编程语言 > Java开发

Vert.x 实现REST

2016-01-16 22:11 441 查看
摘要: * Vert.x Web - 使用Vert.x创建Web应用的框架
* 怎么发布静态资源
* 怎么开发REST API

欢迎关注http://quanke.name/

交流群:
231419585


转载请注明出处,谢谢

回顾

在第一篇文章中开发了一个非常简单的Vert.x 3应用程序,还包括怎么测试、打包和执行。在第二篇文章中对端口进行了可变配置。

这篇文章中,开发一个CRUD(增删改查)应用,发布一个HTML页面,通过REST API与后台进行交互。RESTfull形式的API不简单,这篇文章中就不涉及了。

接下来,能看到:

Vert.x Web - 使用Vert.x创建Web应用的框架

怎么发布静态资源

怎么开发REST API

这篇文章开发的代码放在GitHub上,是从第二篇文章的代码基础上进行的。

开始Vert.x Web

如果你看了前面的文章,使用Vert.x Core来处理复杂的HTTP应用还是很麻烦的,所以就有了Vert.x Web,它可以使Vert.x开发一个web应用更加简单,而且不会改变Vert.x的思想。

更新pom.xml文件,添加下面的依赖:

<dependency>
<groupId>io.vertx</groupId>
<artifactId>vertx-web</artifactId>
<version>3.0.0</version>
</dependency>

这就是使用Vert.x Web的唯一前提。

还记得在上一篇文章中,当请求
http://localhost:8080
时,返回一个Hello World消息,使用Vert.x Web完成同样的事情,打开
name.quanke.study.vertx.first.MyFirstVerticle.java
类,修改
start
方法:

@Override
public void start(Future<Void> fut) {
// Create a router object.
Router router = Router.router(vertx);

// Bind "/" to our hello message - so we are still compatible.
router.route("/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response
.putHeader("content-type", "text/html")
.end("<h1>Hello from my first Vert.x 3 application</h1>");
});

// Create the HTTP server and pass the "accept" method to the request handler.
vertx
.createHttpServer()
.requestHandler(router::accept)
.listen(
// Retrieve the port from the configuration,
// default to 8080.
config().getInteger("http.port", 8080),
result -> {
if (result.succeeded()) {
fut.complete();
} else {
fut.fail(result.cause());
}
}
);
}

在开始start方法里创建了一个
Router
对象。router是Vert.x Web的基础,负责分发HTTP请求到handler(处理器),在Vert.x Web中还有两个很重要的概念。

Routes-定义请求的分发

Handlers-这是实际处理请求并且返回结果的地方。Handlers可以被链接起来使用。

如果明白了这3个概念(Router、Routes、Handlers),就明白了Vert.x Web的所有了。

仔细看看下面这段代码:

router.route("/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
response
.putHeader("content-type", "text/html")
.end("<h1>Hello from my first Vert.x 3 application</h1>");
});

将访问"/"(
http://localhost:8080/
)的请求“路由”到指定的handler。Handlers接收RoutingContext对象。这个handler的方法和我们之前的代码很像,他们操作的是同一个HttpServerResponse类型的对象。

让我们来看看剩下的代码:

vertx
.createHttpServer()
.requestHandler(router::accept)
.listen(
// Retrieve the port from the configuration,
// default to 8080.
config().getInteger("http.port", 8080),
result -> {
if (result.succeeded()) {
fut.complete();
} else {
fut.fail(result.cause());
}
}
);
}

除了改变了request handler,基本和之前的代码一样。传
router::accept
给handler。你可能对这个符号不太熟悉。它表示引用一个方法(这里是引用
router
accept
方法)。换句话说,当接收到一个请求的时候,告诉vert.x从
router
里调用
accept
方法。

让我们来看下它是怎么工作的:

mvn clean package
java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar

浏览器打开
http://localhost:8080
,你会看到Hello的消息。

发布静态资源

现在有了第一个使用Vert.x Web开发的应用。先在写增加一个index.html页面(静态资源)。

这个HTML页面将会是我们应用的入口。在src/main/resources/assets目录下,index.html文件在github上。此文不涉及这个文件的细节。

基本上,就是一个简单的
CRUD
的UI界面,
actions
是由通过
AJAX
调用的
REST API
执行的。

创建完了页面后,编辑name.quanke.study.vertx.first.MyFirstVerticle类,并修改start方法:

@Override
public void start(Future<Void> fut) {
Router router = Router.router(vertx);
router.route("/").handler(routingContext -> { HttpServerResponse response = routingContext.response(); response .putHeader("content-type", "text/html") .end("<h1>Hello from my first Vert.x 3 application</h1>"); });
// Serve static resources from the /assets directory
// 将访问“/assets/*”的请求route到“assets”目录下的资源

router.route("/assets/*").handler(StaticHandler.create("assets"));
vertx .createHttpServer() .requestHandler(router::accept) .listen( // Retrieve the port from the configuration, // default to 8080. config().getInteger("http.port", 8080), result -> { if (result.succeeded()) { fut.complete(); } else { fut.fail(result.cause()); } } ); }

就这段代码和前面的不同:

router.route("/assets/*").handler(StaticHandler.create("assets"));

这一行是什么意思?挺简单的。将访问“/assets/*”的请求route到“assets”目录下的资源。现在可以通过
http://localhost:8080/assets/index.html
来访问index.html了。

测试之前,我们花一些时间来看一下handler的创建。所有的处理请求动作在Vert.x Web里都实现成handler。而创建一个handler需要调用
create
方法。

编译、运行:

mvn clean package
java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar

浏览器,输入
http://localhost:8080/assets/index.html


现在这个table是空的。那是因为我们还没有实现REST的API。现在让我们来开始吧。

使用Vert.x Web实现REST API

Vert.x Web实现REST API很简单。看下面:

GET /api/whiskies => 获取所有的威士忌(getAll)

GET /api/whiskies/:id => 获取指定id的威士忌(getOne)

POST /api/whiskies =>添加一瓶威士忌(addOne)

PUT /api/whiskies/:id => 编辑一瓶威士忌(updateOne)

DELETE /api/whiskies/id => 删除一瓶威士忌(deleteOne)

我们需要一些数据。。。
在实现REST API之前,需要创建Whisky的数据模型。使用下面的内容创建
src/main/java/quanke/name/study/vertx/first/Whisky.java


package name.quanke.study.vertx.first;

import java.util.concurrent.atomic.AtomicInteger;

public class Whisky {

private static final AtomicInteger COUNTER = new AtomicInteger();

private final int id;

private String name;

private String origin;

public Whisky(String name, String origin) {
this.id = COUNTER.getAndIncrement();
this.name = name;
this.origin = origin;
}

public Whisky() {
this.id = COUNTER.getAndIncrement();
}

public String getName() {
return name;
}

public String getOrigin() {
return origin;
}

public int getId() {
return id;
}

public void setName(String name) {
this.name = name;
}

public void setOrigin(String origin) {
this.origin = origin;
}
}

这是一个很简单的bean类。因为Vert.x依赖Jackson来处理JSON格式,Jackson能够自动序列化和反序列化bean类,让代码变得更简单,所以选择这样的格式。

现在,创建几瓶威士忌。在MyFirstVerticle类中,添加下面的代码:

// Store our product
// 存储产品
private Map<Integer, Whisky> products = new LinkedHashMap<>();
// Create some product
// 创建一些产品
private void createSomeData() {
Whisky bowmore = new Whisky("Bowmore 15 Years Laimrig", "Scotland, Islay");
products.put(bowmore.getId(), bowmore);
Whisky talisker = new Whisky("Talisker 57° North", "Scotland, Island");
products.put(talisker.getId(), talisker);
}

然后,在
start
方法里,调用
createSomeData
方法:

@Override
public void start(Future<Void> fut) {

createSomeData();

// Create a router object.
Router router = Router.router(vertx);

// Rest of the method
}

在这里并没有一个后台数据库。仅使用一个map,将数据存储在内存中。添加后端数据库的介绍我准备放在另一篇文章中讲。

获得产品(威士忌)

GET /api/whiskies
,JSON数组中返回产品列表。

start
方法里,添加下面这行(static handler):

router.get("/api/whiskies").handler(this::getAll);

告诉
router
调用
getAll
方法来处理
"/api/whiskies"
的GET请求。代码可以写在
handler
里,但是为了让代码更加清晰,另外创建一个方法:

private void getAll(RoutingContext routingContext) {
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(products.values()));
}

每一个handler(比如:请看上面的代码)都会接受一个
RoutingContext
参数。通过设置
content-type
和一些内容来填充
response
。因为内容可能会碰到特殊的字符,所以强制使用UTF-8的格式。创建内容的时候,并不需要自己去处理JSON格式的字符串。Vert.x有处理Json的API。使用Json.encodePrettily(products.values())处理JSON字符串。本应使用Json.encodePrettily(products),但是为了让JavaScript代码更简单,我们仅返回威士忌(产品)的数据集合,并没有返回包含
Id=>Bottle
的键值对。

打包运行:

mvn clean package
java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar

浏览器访问
http://localhost:8080/assets/index.html
,然后你将会看到下面这个页面。



很好奇,想看一下REST API到底返回了什么。打开浏览器,访问http://localhost:8080/api/whiskies。你会看到下面这样的信息:

[ {
"id" : 0,
"name" : "Bowmore 15 Years Laimrig",
"origin" : "Scotland, Islay"
}, {
"id" : 1,
"name" : "Talisker 57° North",
"origin" : "Scotland, Island"
} ]

创建一个产品

能获取到威士忌(产品)了,现在需要创建一个产品。不像之前的REST API,这一次,需要读取
request
body
。因为性能的原因,它应该被显式地启用。不要怕,这也仅仅是一个
handler
而已。

start
方法中,添加下面的内容到
getAll
的后面:

router.route("/api/whiskies*").handler(BodyHandler.create());
router.post("/api/whiskies").handler(this::addOne);

第一行允许
"/api/whiskies"
下的所有
route
读取请求的
body
。通过使用
router.route().handler(BodyHandler.create())
,能让它在全局生效。

第二行将对
/api/whiskies
的POST请求映射到
addOne
方法。让我们来创建这个方法:

private void addOne(RoutingContext routingContext) {
final Whisky whisky = Json.decodeValue(routingContext.getBodyAsString(),
Whisky.class);
products.put(whisky.getId(), whisky);
routingContext.response()
.setStatusCode(201)
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily(whisky));
}

开始从请求的
body
中取出
Whisky
对象。只是将body读成一个字符串并将它传入到Json.decodeValue方法里。Whisky这个对象一旦创建好,将被添加到后台的map中,并以JSON的格式返回。

重新编译并且运行:

mvn clean package
java -jar target/my-first-app-1.0-SNAPSHOT-fat.jar

刷新HTML页面,点击
Add a new bottle
按钮。输入数据,如:
“quanke”
作为名字, “quanke.name” 作为产地 ,就OK了。

状态码 201 ?
CREATED
和在REST API中创建一个entity时,response的状态码为201。。默认的vert.x web设置一个200的状态码代表OK。

删除一个产品

start
方法里,添加:

router.delete("/api/whiskies/:id").handler(this::deleteOne);

URL
里,参数为:
:id
。在处理一个相匹配的请求的时候,Vert.x提取路径中与这个参数对应的一段,能够在handler中获得。例如,
/api/whiskies/0
id
映射为
0


看一下在
handler
方法中这个参数是怎样被使用的。创建一个
deleteOne
方法。

private void deleteOne(RoutingContext routingContext) {
String id = routingContext.request().getParam("id");
if (id == null) {
routingContext.response().setStatusCode(400).end();
} else {
Integer idAsInteger = Integer.valueOf(id);
products.remove(idAsInteger);
}
routingContext.response().setStatusCode(204).end();
}

状态码 204 ?
状态码为204 - NO CONTENT。HTTP delete动作通常都是无返回内容的。

其他方法

实现getOne和updateOne很简单,和上面的差不多,此文不再详细介绍。源码在github

总结

此文介绍了如何用Vert.x web轻松的实现一个REST API,如何访问静态资源。比以前的文章复杂些,但仍然还是很简单。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  vertx3 vertx javaee