实时Web应用就是前后端通过 WebSocket 来进行交互,非传统的Ajax交互。 下面是Vert.x 官方给的示例
Server.java
package io.vertx.example.web.angular_realtime;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.eventbus.Message;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.example.util.Runner;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.auth.shiro.ShiroAuth;
import io.vertx.ext.auth.shiro.ShiroAuthOptions;
import io.vertx.ext.auth.shiro.ShiroAuthRealmType;
import io.vertx.ext.mongo.MongoClient;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.handler.*;
import io.vertx.ext.web.handler.sockjs.BridgeOptions;
import io.vertx.ext.web.handler.sockjs.PermittedOptions;
import io.vertx.ext.web.handler.sockjs.SockJSHandler;
import io.vertx.ext.web.sstore.LocalSessionStore;
import java.util.LinkedList;
import java.util.List;
/*
* @author <a href="mailto:pmlopes@gmail.com">Paulo Lopes</a>
*/
public class Server extends AbstractVerticle {
public static void main(String[] args) {
Runner.runExample(Server.class);
}
private MongoClient mongo;
@Override
public void start() throws Exception {
mongo = MongoClient.createShared(vertx, new JsonObject().put("db_name", "demo"));
loadData(mongo);
vertx.eventBus().consumer("vtoons.listAlbums", this::listAlbums);
vertx.eventBus().consumer("vtoons.placeOrder", this::placeOrder);
Router router = Router.router(vertx);
router.route().handler(CookieHandler.create());
router.route().handler(BodyHandler.create());
router.route().handler(SessionHandler.create(LocalSessionStore.create(vertx)));
router.post("/login").handler(ctx -> {
JsonObject credentials = ctx.getBodyAsJson();
if (credentials == null) {
ctx.fail(400);
return;
}
ctx.response().end("ok");
});
router.route("/eventbus/*").handler(ctx -> {
ctx.next();
});
BridgeOptions options = new BridgeOptions()
.addInboundPermitted(new PermittedOptions()
.setAddress("vtoons.listAlbums")) // 和之前配路由地址很像, 前端像这个 地址发送消息,Server进行处理
.addInboundPermitted(new PermittedOptions()
.setAddress("vtoons.login"))
.addInboundPermitted(new PermittedOptions()
.setAddress("vtoons.placeOrder")
.setRequiredAuthority("place_order"))
.addOutboundPermitted(new PermittedOptions());
router.route("/eventbus/*").handler(SockJSHandler.create(vertx).bridge(options));
router.route().handler(StaticHandler.create());
vertx.createHttpServer().requestHandler(router::accept).listen(8080);
}
private void listAlbums(Message<JsonObject> msg) {
// issue a find command to mongo to fetch all documents from the "albums" collection.
mongo.find("albums", new JsonObject(), lookup -> {
// error handling
if (lookup.failed()) {
msg.fail(500, lookup.cause().getMessage());
return;
}
// now convert the list to a JsonArray because it will be easier to encode the final object as the response.
final JsonArray json = new JsonArray();
for (JsonObject o : lookup.result()) {
json.add(o);
}
msg.reply(json);
});
}
private void placeOrder(Message<JsonObject> msg) {
mongo.save("orders", msg.body(), save -> {
// error handling
if (save.failed()) {
msg.fail(500, save.cause().getMessage());
return;
}
msg.reply(new JsonObject());
});
}
private void loadData(MongoClient db) {
db.dropCollection("albums", drop -> {
if (drop.failed()) {
throw new RuntimeException(drop.cause());
}
List<JsonObject> albums = new LinkedList<>();
albums.add(new JsonObject()
.put("artist", "The Wurzels")
.put("genre", "Scrumpy and Western")
.put("title", "I Am A Cider Drinker")
.put("price", 0.99));
albums.add(new JsonObject()
.put("artist", "Vanilla Ice")
.put("genre", "Hip Hop")
.put("title", "Ice Ice Baby")
.put("price", 0.01));
albums.add(new JsonObject()
.put("artist", "Ena Baga")
.put("genre", "Easy Listening")
.put("title", "The Happy Hammond")
.put("price", 0.50));
albums.add(new JsonObject()
.put("artist", "The Tweets")
.put("genre", "Bird related songs")
.put("title", "The Birdy Song")
.put("price", 1.20));
for (JsonObject album : albums) {
db.insert("albums", album, res -> {
System.out.println("inserted " + album.encode());
});
}
});
}
}
复制代码
client_app.js
/*
* Copyright 2011-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
function CartController($scope, $filter, $http, $timeout) {
$scope.items = [];
$scope.orderSubmitted = false;
$scope.username = '';
$scope.password = '';
$scope.loggedIn = false;
$scope.eb = null;
$scope.addToCart = function(album) {
console.log("Adding to cart: " + JSON.stringify(album));
for (var i = 0; i < $scope.items.length; i++) {
var compare = $scope.items[i];
if (compare.album._id === album._id) {
compare.quantity = compare.quantity + 1;
return;
}
}
var item = {
album: album,
quantity: 1
};
$scope.items.push(item);
};
$scope.removeFromCart = function(item) {
$scope.items = $scope.items.filter( function(v) { return v.album._id !== item.album._id; });
};
$scope.total = function() {
var tot = 0;
for (var i = 0; i < $scope.items.length; i++) {
var item = $scope.items[i];
tot += item.quantity * item.album.price;
}
return tot;
};
$scope.orderReady = function() {
return $scope.items.length > 0 && $scope.loggedIn;
};
$scope.submitOrder = function() {
if (!$scope.orderReady()) {
return;
}
var orderItems = $filter('json')($scope.items);
var orderMsg = {
username: $scope.username,
items: orderItems
};
$scope.eb.send('vtoons.placeOrder', orderMsg, function(err, reply) {
if (err) {
console.error('Failed to accept order');
return;
}
$scope.orderSubmitted = true;
// lets clear the cart now
$scope.items = [];
$scope.$apply();
// Timeout the order confirmation box after 2 seconds
// window.setTimeout(function() { $scope.orderSubmitted(false); }, 2000);
});
};
$scope.login = function() {
if ($scope.username.trim() != '' && $scope.password.trim() != '') {
$http.post(window.location.protocol + '//' + window.location.hostname + ':' + window.location.port + '/login', {username: $scope.username, password: $scope.password}).success(function(data, status) {
$scope.loggedIn = true;
$timeout(function() {
if ($scope.eb != null) {
$scope.eb.close();
}
$scope.eb = new EventBus(window.location.protocol + '//' + window.location.hostname + ':' + window.location.port + '/eventbus');
$scope.eb.onopen = function() {
// Get the static data
$scope.eb.send('vtoons.listAlbums', {}, function(err, reply) {
if (err) {
console.error('Failed to retrieve albums: ' + err);
return;
}
$scope.albums = reply.body;
$scope.$apply();
});
};
$scope.eb.onclose = function() {
$scope.eb = null;
};
$scope.$apply();
});
}).error(function () {
alert('invalid login');
});
}
};
}
复制代码
index.html
<!--
~ Copyright 2011-2012 the original author or authors.
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
<html ng-app>
<head>
<link rel="stylesheet/less" href="css/bootstrap.less">
<script src="js3rdparty/less-1.2.1.min.js"></script>
<script src='js3rdparty/jquery-1.7.1.min.js'></script>
<script src='js3rdparty/bootstrap-tabs.js'></script>
<title>Welcome to vToons</title>
</head>
<body>
<div class="container-fluid" ng-controller="CartController">
<div class="sidebar">
<div class="well">
<h2>Order total:</h2>
<h2><strong ng-bind="total() | currency"></strong></h2>
</div>
<form class="form-stacked" ng-show="!loggedIn">
<label for="username">Username</label>
<input type="text" id="username" class="span2"
ng-model="username"/>
<label for="password">Password</label>
<input type="password" id="password" class="span2"
ng-model="password"/>
<br><br>
<input type="submit" class="btn primary" value="Login"
ng-click="login()"/>
</form>
<br>
<div ng-show="loggedIn">
<h3>Logged in as</h3>
<h3><strong ng-bind="username"></strong></h3>
</div>
</div>
<div class="content">
<div class="hero-unit">
<h1>Welcome to vToons</h1>
<p>I hope you will enjoy our fantastic selection of music</p>
</div>
<div class="row" ng-show="loggedIn">
<ul class="tabs">
<li class="active"><a href="#shop">Shop</a></li>
<li><a href="#cart">Cart</a></li>
</ul>
<div class="pill-content">
<div class="active" id="shop">
<div class="span16">
<h2>Please choose from our wonderful selection of songs:</h2>
<table class="bordered-table">
<thead>
<tr>
<th>Genre</th>
<th>Artist</th>
<th>Album</th>
<th>Price</th>
</tr>
</thead>
<tbody ng-repeat="album in albums">
<tr>
<td ng-bind="album.genre"></td>
<td ng-bind="album.artist"></td>
<td ng-bind="album.title"></td>
<td ng-bind="album.price | currency"></td>
<td><a href="#" ng-click="addToCart(album)">Add to
Cart</a></td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="cart">
<div class="alert-message info fade in"
ng-show="orderSubmitted">
<p><strong>Your order has been accepted!</strong></p>
</div>
<div class="alert-message warn fade in"
ng-show="!orderReady()">
<p><strong>Please add some item(s) to your cart and login before
submitting.</strong></p>
</div>
<div class="span16">
<form>
<fieldset>
<legend>Cart</legend>
<div class="actions">
<input type="submit" class="btn primary" value="Submit Order"
ng-click="submitOrder()"/>
</div>
</fieldset>
</form>
</div>
<div class="span16">
<table class="bordered-table">
<thead>
<tr>
<th>Genre</th>
<th>Artist</th>
<th>Album</th>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody ng-repeat="item in items">
<tr>
<td ng-bind="item.album.genre"></td>
<td ng-bind="item.album.artist"></td>
<td ng-bind="item.album.title"></td>
<td ng-bind="item.album.price | currency"></td>
<td ng-bind="item.quantity"></td>
<td><a href="#"
ng-click="removeFromCart(item)">Remove</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
$(function () {
$('.tabs').tabs();
})
</script>
<script src='js3rdparty/sockjs-min-0.3.4.js'></script>
<script src='js3rdparty/angular-1.0.1.js'></script>
<script src='js/vertx-eventbus.js'></script>
<script src='js/client_app.js'></script>
</html>
复制代码
现在配置的地址 vtoons.listAlbums
,就相当于Vert.x里面的路由,Spring MVC里的Controller,前端对这个地址发送消息,后端接受,然后处理,返回给前端,同时在某些推送场合,后端也可以主动给前端推送消息