> For the complete documentation index, see [llms.txt](https://potoyang.gitbook.io/spring-in-action-v4/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://potoyang.gitbook.io/spring-in-action-v4/di-5-zhang-gou-jian-spring-web-ying-yong-cheng-xu/untitled-2/untitled.md).

# 5.3.2　通过路径参数接受输入

假设我们的应用程序需要根据给定的 ID 来展现某一个 Spittle 记录。其中一种方案就是编写处理器方法，通过使用 @RequestParam 注解，让它接受 ID 作为查询参数：

```java
@RequestMapping(value="/show", method=RequestMethod.GET)
public String showSpittles(
    @RequestParam("spittle_id") long spittleId, 
    Model model) {
  model.addAttribute(spittleRepository.findOne(spittleId));
  return "spittle";
}
```

这个处理器方法将会处理形如 `/spittles/show?spittle_id=12345` 这样的请求。尽管这也可以正常工作，但是从面向资源的角度来看这并不理想。在理想情况下，要识别的资源（Spittle）应该通过 URL 路径进行标示，而不是通过查询参数。对 `/spittles/12345` 发起 GET 请求要优于对 `/spittles/show?spittle_id=12345` 发起请求。前者能够识别出要查询的资源，而后者描述的是带有参数的一个操作 —— 本质上是通过 HTTP 发起的 RPC。

既然已经以面向资源的控制器作为目标，那我们将这个需求转换为一 测试。程序清单 5.12 展现了一个新的测试方法，它会断言 SpittleController中 对面向资源请求的处理。

{% code title="程序清单 5.12　测试对某个 Spittle 的请求，其中 ID 要在路径变量中指定" %}

```java
@Test
public void testSpittle() throws Exception {
  Spittle expectedSpittle = new Spittle("Hello", new Date());
  SpittleRepository mockRepository = mock(SpittleRepository.class);
  when(mockRepository.findOne(12345)).thenReturn(expectedSpittle);
    
  SpittleController controller = new SpittleController(mockRepository);
  MockMvc mockMvc = standaloneSetup(controller).build();

  mockMvc.perform(get("/spittles/12345"))
    .andExpect(view().name("spittle"))
    .andExpect(model().attributeExists("spittle"))
    .andExpect(model().attribute("spittle", expectedSpittle));
}
```

{% endcode %}

可以看到，这个测试构建了一个 mock Repository、一个控制器和 MockMvc，这与本章中我们所编写的其他测试很类似。这个测试中最重要的部分是最后几行，它对 `/spittles/12345` 发起 GET 请求，然后断言视图的名称是 spittle，并且预期的 Spittle 对象放到了模型之中。因为我们还没有为这种请求实现处理器方法，因此这个请求将会失败。但是，我们可以通过为 SpittleController 添加新的方法来修正这个失败的测试。

到目前为止，在我们编写的控制器中，所有的方法都映射到了（通过 @RequestMapping）静态定义好的路径上。但是，如果想让这个测试通过的话，我们编写的 @RequestMapping 要包含变量部分，这部分代表了 Spittle ID。

为了实现这种路径变量，Spring MVC 允许我们在 @RequestMapping 路径中添加占位符。占位符的名称要用大括号（`{` 和 `}`）括起来。路径中的其他部分要与所处理的请求完全匹配，但是占位符部分可以是任意的值。

下面的处理器方法使用了占位符，将 Spittle ID 作为路径的一部 分：

```java
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
public String spittle(
    @PathVariable("spittleId") long spittleId, 
    Model model) {
  model.addAttribute(spittleRepository.findOne(spittleId));
  return "spittle";
}
```

例如，它就能够处理针对 `/spittles/12345` 的请求，也就是程序清单 5.12 中的路径我们可以看到，spittle() 方法的 spittleId 参数上添加了 @Path-Variable("spittleId") 注解，这表明在请求路径中，不管占位符部分的值是什么都会传递到处理器方法的 spittleId 参数中。如果对 `/spittles/54321` 发送 GET 请求，那么将会把 `54321` 传递进来，作为 spittleId 的值。

需要注意的是：在样例中 spittleId 这个词出现了好几次：先是在@Request-Mapping 的路径中，然后作为 @PathVariable 属性的值，最后又作为方法的参数名称。因为方法的参数名碰巧与占位符的名称相同，因此我们可以去掉 @PathVariable 中的 value 属性：

```java
@RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
public String spittle(@PathVariable long spittleId, Model model) {
  model.addAttribute(spittleRepository.findOne(spittleId));
  return "spittle";
}
```

如果 @PathVariable 中没有 value 属性的话，它会假设占位符的名称与方法的参数名相同。这能够让代码稍微简洁一些，因为不必重复写占位符的名称了。但需要注意的是，如果你想要重命名参数时，必须要同时修改占位符的名称，使其互相匹配。

spittle() 方法会将参数传递到 SpittleRepository 的 findOne() 方法中，用来获取某个 Spittle 对象，然后将 Spittle 对象添加到模型中。模型的 key 将会是 spittle，这是根据传递到 addAttribute() 方法中的类型推断得到的。

这样 Spittle 对象中的数据就可以渲染到视图中了，此时需要引用请求中 key 为 spittle 的属性（与模型的 key 一致）。如下为渲染 Spittle 的 JSP 视图片段：

```markup
<div class="spittleViwe">
  <div class="spittleMessage">
    <c:out value="${spittle.message}" />
  </div>
  <div>
    <span class="spittleTime">
      <c:out value="${spittle.time}" />
    </span>
  </div>
</div>
```

这个视图并没有什么特别之处，它的屏幕截图如图 5.4 所示。

![图 5.4　在浏览器中展现一个 spittle](https://3784153818-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LmcjU5gG__lBrRbUxBO%2F-LmrOWPO9pOQa4phmsWM%2F-LmrRd5N5Wkf-pTeJx-Z%2F5.4%20spitlle.jpg?alt=media\&token=7631a03a-88c5-4612-8118-ff35af5394f3)

如果传递请求中少量的数据，那查询参数和路径变量是很合适的。但通常我们还需要传递很多的数据（也许是表单提交的数据），那查询参数显得有些笨拙和受限了。下面让我们来看一下如何编写控制器方法来处理表单提交。
