5.3.2 通过路径参数接受输入
假设我们的应用程序需要根据给定的 ID 来展现某一个 Spittle 记录。其中一种方案就是编写处理器方法,通过使用 @RequestParam 注解,让它接受 ID 作为查询参数:
1
@RequestMapping(value="/show", method=RequestMethod.GET)
2
public String showSpittles(
3
@RequestParam("spittle_id") long spittleId,
4
Model model) {
5
model.addAttribute(spittleRepository.findOne(spittleId));
6
return "spittle";
7
}
Copied!
这个处理器方法将会处理形如 /spittles/show?spittle_id=12345 这样的请求。尽管这也可以正常工作,但是从面向资源的角度来看这并不理想。在理想情况下,要识别的资源(Spittle)应该通过 URL 路径进行标示,而不是通过查询参数。对 /spittles/12345 发起 GET 请求要优于对 /spittles/show?spittle_id=12345 发起请求。前者能够识别出要查询的资源,而后者描述的是带有参数的一个操作 —— 本质上是通过 HTTP 发起的 RPC。
既然已经以面向资源的控制器作为目标,那我们将这个需求转换为一 测试。程序清单 5.12 展现了一个新的测试方法,它会断言 SpittleController中 对面向资源请求的处理。
程序清单 5.12 测试对某个 Spittle 的请求,其中 ID 要在路径变量中指定
1
@Test
2
public void testSpittle() throws Exception {
3
Spittle expectedSpittle = new Spittle("Hello", new Date());
4
SpittleRepository mockRepository = mock(SpittleRepository.class);
5
when(mockRepository.findOne(12345)).thenReturn(expectedSpittle);
6
7
SpittleController controller = new SpittleController(mockRepository);
8
MockMvc mockMvc = standaloneSetup(controller).build();
9
10
mockMvc.perform(get("/spittles/12345"))
11
.andExpect(view().name("spittle"))
12
.andExpect(model().attributeExists("spittle"))
13
.andExpect(model().attribute("spittle", expectedSpittle));
14
}
Copied!
可以看到,这个测试构建了一个 mock Repository、一个控制器和 MockMvc,这与本章中我们所编写的其他测试很类似。这个测试中最重要的部分是最后几行,它对 /spittles/12345 发起 GET 请求,然后断言视图的名称是 spittle,并且预期的 Spittle 对象放到了模型之中。因为我们还没有为这种请求实现处理器方法,因此这个请求将会失败。但是,我们可以通过为 SpittleController 添加新的方法来修正这个失败的测试。
到目前为止,在我们编写的控制器中,所有的方法都映射到了(通过 @RequestMapping)静态定义好的路径上。但是,如果想让这个测试通过的话,我们编写的 @RequestMapping 要包含变量部分,这部分代表了 Spittle ID。
为了实现这种路径变量,Spring MVC 允许我们在 @RequestMapping 路径中添加占位符。占位符的名称要用大括号({})括起来。路径中的其他部分要与所处理的请求完全匹配,但是占位符部分可以是任意的值。
下面的处理器方法使用了占位符,将 Spittle ID 作为路径的一部 分:
1
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
2
public String spittle(
3
@PathVariable("spittleId") long spittleId,
4
Model model) {
5
model.addAttribute(spittleRepository.findOne(spittleId));
6
return "spittle";
7
}
Copied!
例如,它就能够处理针对 /spittles/12345 的请求,也就是程序清单 5.12 中的路径我们可以看到,spittle() 方法的 spittleId 参数上添加了 @Path-Variable("spittleId") 注解,这表明在请求路径中,不管占位符部分的值是什么都会传递到处理器方法的 spittleId 参数中。如果对 /spittles/54321 发送 GET 请求,那么将会把 54321 传递进来,作为 spittleId 的值。
需要注意的是:在样例中 spittleId 这个词出现了好几次:先是在@Request-Mapping 的路径中,然后作为 @PathVariable 属性的值,最后又作为方法的参数名称。因为方法的参数名碰巧与占位符的名称相同,因此我们可以去掉 @PathVariable 中的 value 属性:
1
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
2
public String spittle(@PathVariable long spittleId, Model model) {
3
model.addAttribute(spittleRepository.findOne(spittleId));
4
return "spittle";
5
}
Copied!
如果 @PathVariable 中没有 value 属性的话,它会假设占位符的名称与方法的参数名相同。这能够让代码稍微简洁一些,因为不必重复写占位符的名称了。但需要注意的是,如果你想要重命名参数时,必须要同时修改占位符的名称,使其互相匹配。
spittle() 方法会将参数传递到 SpittleRepository 的 findOne() 方法中,用来获取某个 Spittle 对象,然后将 Spittle 对象添加到模型中。模型的 key 将会是 spittle,这是根据传递到 addAttribute() 方法中的类型推断得到的。
这样 Spittle 对象中的数据就可以渲染到视图中了,此时需要引用请求中 key 为 spittle 的属性(与模型的 key 一致)。如下为渲染 Spittle 的 JSP 视图片段:
1
<div class="spittleViwe">
2
<div class="spittleMessage">
3
<c:out value="${spittle.message}" />
4
</div>
5
<div>
6
<span class="spittleTime">
7
<c:out value="${spittle.time}" />
8
</span>
9
</div>
10
</div>
Copied!
这个视图并没有什么特别之处,它的屏幕截图如图 5.4 所示。
图 5.4 在浏览器中展现一个 spittle
如果传递请求中少量的数据,那查询参数和路径变量是很合适的。但通常我们还需要传递很多的数据(也许是表单提交的数据),那查询参数显得有些笨拙和受限了。下面让我们来看一下如何编写控制器方法来处理表单提交。
Last modified 2yr ago
Copy link