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

使用 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,其他成员变量 restaurants、categories 和 category 构成页面的数据模型。视图中使用了变量 restaurants 和 categories。
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 方法。 |
|
|
|
|
|