首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

使用 Google Sitebricks 构建更好的 Web 应用程序(4)

使用 Google Sitebricks 构建更好的 Web 应用程序(4)

显示结果应用程序的第一页将展示系统中的所有餐馆,并让用户基于餐馆供应的食品类型筛选餐馆列表。Sitebricks 是一种模型-视图-控制(MVC)类型的框架,不过它专注于应用程序中的控制器和视图。控制器是简单的 Java 类。显示所有餐馆的控制器如清单 7 所示。
清单 7. ShowRestaurants 控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@At("/")
public class ShowRestaurants {
    private List<Restaurant> restaurants;
    private String category;
    private LinkedHashSet<String> categories;
    private final RestaurantDao dao;

    @Inject
    public ShowRestaurants(RestaurantDao dao){
        this.dao = dao;
    }
     
    @Get
    public void get(){
        this.restaurants = dao.findAll();
        categories = new LinkedHashSet<String>(restaurants.size());
        for (Restaurant restaurant : restaurants){
            categories.add(restaurant.getCategory());
        }
        if (category != null && category.trim().length() > 0){
            List<Restaurant> filtered = new ArrayList<Restaurant>
(restaurants.size());
            for (Restaurant restaurant : restaurants){
                if (restaurant.getCategory().equalsIgnoreCase(category)){
                    filtered.add(restaurant);
                }
            }
            restaurants = filtered;
        }
    }
    // getters and setters omitted for brevity
}




注意,该类由 @At 注释,这是一个 Sitebricks 注释。"/" 值告诉 Sitebricks 将 "/" 的任何传入请求映射到该类。这个类充当应用程序 “主页” 的控制器。
Sitebricks 密切遵循 HTTP 和 REST 约定,因此能将不同的方法用于不同的 HTTP 方法,比如 GET 和 POST。@Get 注释表明要为 GET 请求使用哪些方法。您可以随意命名方法;“get” 没有任何特殊之处。注释决定将调用什么方法。
上述类有多个成员变量。RestaurantDao 是一个用来获取页面所需数据的对象。@Inject 注释下的构造函数 ShowRestaurants 告诉 Guice 注入一个 RestaurantDao,其他成员变量 restaurantscategoriescategory 构成页面的数据模型。视图中使用了变量 restaurantscategories
category 变量实际上是一个请求参数。Sitebricks 会自动将同名的请求变量绑定到该参数。例如,如果为 http://<app>?category=Thai 发出请求,那么类别就是 Thai。根据代码,如果有一个给定类别,那么它将用于筛选所显示的餐馆列表。
清单 8 显示了视图代码,它使用控制器构建的数据模型。
清单 8. ShowRestaurants 视图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">

<html lang="en">
  <head>
    <title>Reviewr</title>
  </head>
  <body>
      <h1>All Restaurants</h1>
      <form method="get">
          <label for="category">Select category:</label>
          <select name="category" id="category">
              <option value="" label="Any"/>
              @Repeat(items=categories, var="category")
              <option>${category}</option>
          </select>
          <input type="submit" value="Filter"/>
      </form>
      <table border="1">
          <thead>
              <tr>
                  <td>Name</td>
                  <td>Category</td>
                  <td>Average Rating</td>
              </tr>
          </thead>
          <tbody>
              @Repeat(items=restaurants, var="restaurant")
              <tr><td><a
href="/restaurant/${restaurant.name}">${restaurant.name}</a></td>
<td>${restaurant.category}</td><td>${restaurant.averageRating}
</td></tr>
          </tbody>
      </table>
      <div class="msg">Not in the list?
          <a href="/restaurants/new">Add a restaurant!</a>
      </div>
  </body>
</html>




上面所示的 ShowRestaurants.html 命名匹配控制器。由于 Sitebricks 使用了该约定,您不必通过配置来完成它。HTML 主要是纯 HTML;没有脚本片段(这不是一个 JSP)。Sitebricks 小部件 @Repeat 用来遍历集合。评估小部件使用的是 MVEL 表达式语言。MVEL 是典型的 JSP/JSF 表达式语言的一个超集。代码创建一个餐馆表,其中每个餐馆的名称也是到 /restaurant/${restaurant.name} 的超链接。例如,如果有一个名为 TCBY 的餐馆,相应的链接就是 /restaurant/TCBY。像这样的动态 URLs 是 Sitebricks 的基本组成部分。清单 9 显示了它们如何处理示例中使用的控制器。
清单 9. RestaurantDetails 控制器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
@At("/restaurant/:name")
public class RestaurantDetails {
    private final RestaurantDao dao;
    private final ReviewDao reviewDao;
    private final Logger logger;
    private Restaurant restaurant;
    private String text;
    private String author;
    private Integer rating;
    private Integer restaurantId;

    @Inject
    public RestaurantDetails(RestaurantDao dao, ReviewDao reviewDao, Logger logger){
        this.dao = dao;
        this.reviewDao = reviewDao;
        this.logger = logger;
    }

    @Get
    public void get(@Named("name") String name){
        try {
            name = URLDecoder.decode(name, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        this.restaurant = dao.findByName(name);
        reviewDao.getReviewsForRestaurant(restaurant);
    }

    @Post
    public String addReview(@Named("name") String name){
        try {
            name = URLDecoder.decode(name, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
            logger.log(Level.SEVERE, null, ex);
        }
        restaurant = dao.findByName(name);
        reviewDao.create(restaurant, text, author, rating);
        return "/reviewrWeb/restaurant/"+restaurant.getName();
    }
     
    public boolean getNoReviews() {
        return this.restaurant.getReviews().size() == 0;
    }
    // getters and setters omitted for brevity   
}




再次注意一下 @At 注释。这次它的值是 "/restaurant/:name"。:name 用于告诉 Sitebricks 这是个动态变量。它将 URL 与该模式匹配,并将 URL 的最后部分放到一个名为 name 的变量中。这是 Guice 中的常用变量。
get 方法有一个输入参数,由 @Named("name") 注释。调用 get 方法(收到 GET 请求时会调用该方法,因为它含有 @Get 注释)时,这会使 Guice 将从 URL 提取的值注入到该变量中。
示例对变量执行 URL 解码,因为它作为 URL 的一部分引入,而非作为一个 HTTP 请求参数(已执行解码)。如果没有它,Elephant Bar 会以 Elephant%20Bar 的形式引入。您可能会注意到,该类也包含一个有 @Post 注释的 addReview 方法。
返回列表