Vert.x & Angular 实时Web应用

实时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,前端对这个地址发送消息,后端接受,然后处理,返回给前端,同时在某些推送场合,后端也可以主动给前端推送消息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值