2. 概述
本教程将指导你在Spring中设置REST API,包括控制器实现、HTTP响应码处理、请求/响应体序列化配置以及内容协商机制。
3. 依赖
使用Spring Boot构建REST API时,只需添加Spring Boot Starter Web依赖即可。它已内置Web开发所需的核心库(HTTP请求处理、JSON序列化等):
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Spring Boot会自动将Jackson配置为默认的JSON序列化/反序列化工具。
4. 控制器
@RestController
是RESTful API中Web层的核心组件。本文示例实现了一个简单的资源控制器FooController
:
@RestController
@RequestMapping("/foos")
class FooController {
@Autowired
private IFooService service;
@GetMapping
public List<Foo> findAll() {
return service.findAll();
}
@GetMapping(value = "/{id}")
public Foo findById(@PathVariable("id") Long id) {
return RestPreconditions.checkFound(service.findById(id));
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Long create(@RequestBody Foo resource) {
Preconditions.checkNotNull(resource);
return service.create(resource);
}
@PutMapping(value = "/{id}")
@ResponseStatus(HttpStatus.OK)
public void update(@PathVariable( "id" ) Long id, @RequestBody Foo resource) {
Preconditions.checkNotNull(resource);
RestPreconditions.checkNotNull(service.getById(resource.getId()));
service.update(resource);
}
@DeleteMapping(value = "/{id}")
@ResponseStatus(HttpStatus.OK)
public void delete(@PathVariable("id") Long id) {
service.deleteById(id);
}
}
这里使用了Guava风格的工具类RestPreconditions
做前置校验:
public class RestPreconditions {
public static <T> T checkFound(T resource) {
if (resource == null) {
throw new MyResourceNotFoundException();
}
return resource;
}
}
**控制器类可以不声明为public
**——因为它通常位于依赖链末端,只需接收DispatcherServlet
转发的请求并委托给服务层。除非需要直接注入控制器,否则没必要用public
修饰。
请求映射规则很直观:**@RequestMapping
的value
和HTTP方法共同决定目标处理方法**。@RequestBody
将HTTP请求体绑定到方法参数,@ResponseBody
则将返回对象序列化为响应体。
@RestController
相当于@Controller
+ @ResponseBody
的组合注解,确保资源通过合适的HTTP转换器进行序列化/反序列化。内容协商机制会根据Accept
等HTTP头自动选择转换器。
5. 测试Spring上下文
Spring Boot的自动配置和测试注解极大简化了测试流程:
✅ 全应用上下文测试(不启动服务器):
@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class FooControllerAppIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void whenTestApp_thenEmptyResponse() throws Exception {
this.mockMvc.perform(get("/foos")
.andExpect(status().isOk())
.andExpect(...);
}
}
✅ 仅测试Web层(避免加载无关组件):
@RunWith(SpringRunner.class)
@WebMvcTest(FooController.class)
public class FooControllerWebLayerIntegrationTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private IFooService service;
@Test()
public void whenTestMvcController_thenRetrieveExpectedResult() throws Exception {
// ...
this.mockMvc.perform(get("/foos")
.andExpect(...);
}
}
使用@WebMvcTest
时,Spring Boot会自动初始化仅包含Controller层及其依赖(如Mock服务)的精简上下文。
6. 映射HTTP响应码
HTTP响应码是REST服务的核心要素,处理不当可能直接导致服务不可用。
6.1 未映射的请求
当Spring MVC收到无匹配映射的请求时:
- 返回
405 METHOD NOT ALLOWED
- 自动添加
Allow
响应头(声明支持的HTTP方法) - 这是Spring MVC的默认行为,无需额外配置
6.2 有效的映射请求
所有已映射的请求默认返回200 OK
。因此示例控制器中:
create/update/delete
操作通过@ResponseStatus
显式声明状态码get
操作保持默认的200 OK
6.3 客户端错误
自定义异常可直接映射为HTTP状态码。在Web层任意位置抛出这些异常即可:
@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
//
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
//
}
⚠️ 注意:
- 这些异常应仅用于REST相关层(如Controller/Service)
- DAO/DAL层不应直接使用
- 遵循Spring惯例,使用运行时异常(非检查异常)
6.4 使用@ExceptionHandler
另一种方式是在控制器中使用@ExceptionHandler
:
@ControllerAdvice
public class CustomExceptionHandler {
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public void handleResourceNotFound() {}
}
❌ 缺点:需在每个控制器中重复定义。更灵活的方案可参考Spring异常处理机制。
7. 结论
本文演示了如何使用Spring和Java配置实现REST服务。后续系列将深入探讨API可发现性、高级内容协商及资源多表示形式处理。