4.1 Spring MVC集成
文字讲解
Spring Boot通过spring-boot-starter-web自动集成了Spring MVC框架。当这个Starter在类路径中时,WebMvcAutoConfiguration会自动配置Spring MVC所需的核心组件,包括:
DispatcherServlet:作为前端控制器,接收所有请求并将其分派给相应的处理器(Controller)。HandlerMapping:根据请求的URL、HTTP方法等信息,找到处理该请求的@Controller方法。HandlerAdapter:负责调用找到的处理器方法。ViewResolver:解析处理器方法返回的逻辑视图名,并渲染成最终的视图(在RESTful API中通常是JSON)。HttpMessageConverter:负责将请求体(如JSON)转换为Java对象,以及将Java对象转换为响应体(如JSON)。Jackson2HttpMessageConverter是默认处理JSON的转换器。
架构图:Spring MVC请求处理流程
4.2 RESTful API设计与实现
文字讲解
REST (Representational State Transfer) 是一种软件架构风格,而不是一个标准。一个符合REST风格的API(即RESTful API)应该遵循以下原则:
- 资源(Resource):API的核心是资源,通过URL来标识。例如,
/users代表用户集合,/users/1代表ID为1的用户。 - 统一接口(Uniform Interface):使用标准的HTTP方法来操作资源:
GET:获取资源。POST:创建新资源。PUT:更新或替换整个资源。PATCH:部分更新资源。DELETE:删除资源。
- 无状态(Stateless):服务器不应保存客户端的会话状态。每个请求都应包含所有必要的信息。
- HATEOAS (Hypermedia as the Engine of Application State):响应中应包含相关资源的链接,引导客户端进行下一步操作。
代码示例
创建一个UserController来管理用户资源。
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
private final Map<Long, User> userStore = new ConcurrentHashMap<>();
// GET /api/v1/users/{id} - 获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
User user = userStore.get(id);
return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
}
// POST /api/v1/users - 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User newUser) {
// 实际应用中ID应由数据库生成
Long id = System.currentTimeMillis();
newUser.setId(id);
userStore.put(id, newUser);
return ResponseEntity.status(HttpStatus.CREATED).body(newUser);
}
// PUT /api/v1/users/{id} - 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User updatedUser) {
if (!userStore.containsKey(id)) {
return ResponseEntity.notFound().build();
}
updatedUser.setId(id);
userStore.put(id, updatedUser);
return ResponseEntity.ok(updatedUser);
}
// DELETE /api/v1/users/{id} - 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
if (userStore.remove(id) == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.noContent().build();
}
}
// User DTO
public class User {
private Long id;
private String name;
// getters and setters
}
代码解释:
@RequestMapping("/api/v1/users"):为整个控制器设置了基础路径。@PathVariable:从URL路径中提取变量。@RequestBody:将HTTP请求的JSON体反序列化为User对象。ResponseEntity:一个强大的类,可以完全控制HTTP响应,包括状态码、头部和响应体。
4.3 参数验证与异常处理
文字讲解
参数验证:为了保证数据的完整性和安全性,对传入的参数进行验证至关重要。Spring Boot通过集成hibernate-validator(spring-boot-starter-web已包含)提供了对JSR-303/JSR-380(Bean Validation)的支持。
全局异常处理:为了避免在每个Controller方法中都写try-catch块,并向客户端提供统一、友好的错误响应,我们应该使用全局异常处理机制。@RestControllerAdvice注解可以实现这一点。
代码示例
1. 添加验证注解到DTO
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Email;
import javax.validation.constraints.Size;
public class CreateUserRequest {
@NotBlank(message = "Username cannot be blank")
@Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters")
private String username;
@NotBlank(message = "Email cannot be blank")
@Email(message = "Invalid email format")
private String email;
// getters and setters
}
2. 在Controller中启用验证
import javax.validation.Valid;
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody CreateUserRequest request) {
// ...
}
3. 创建全局异常处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public Map<String, String> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Map<String, String> handleAllExceptions(Exception ex) {
Map<String, String> response = new HashMap<>();
response.put("error", "An unexpected error occurred");
response.put("message", ex.getMessage());
return response;
}
}
代码解释:
@Valid注解触发对CreateUserRequest对象的验证。- 如果验证失败,Spring MVC会抛出
MethodArgumentNotValidException。 @RestControllerAdvice类中的@ExceptionHandler方法会捕获这个异常,并返回一个包含所有字段错误的结构化JSON响应,状态码为400 (Bad Request)。
4.4 跨域处理(CORS)
文字讲解
出于安全原因,浏览器限制从一个源(Origin)发起的跨源HTTP请求。CORS(Cross-Origin Resource Sharing) 是一种机制,它使用额外的HTTP头来告诉浏览器,让运行在一个源上的Web应用被准许访问来自不同源服务器的选定资源。
Spring Boot提供了多种配置CORS的方式,最常用的是通过@CrossOrigin注解或全局配置。
代码示例
1. @CrossOrigin注解(局部配置)
@RestController
@RequestMapping("/api/v1/public-data")
public class PublicDataController {
@CrossOrigin(origins = "http://example.com")
@GetMapping
public String getPublicData() {
return "This is public data.";
}
}
2. 全局配置(推荐)
创建一个配置类来实现WebMvcConfigurer接口。
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**") // 对所有/api/下的路径生效
.allowedOrigins("http://localhost:3000", "https://my-frontend.com") // 允许的源
.allowedMethods("GET", "POST", "PUT", "DELETE") // 允许的方法
.allowedHeaders("*") // 允许的头
.allowCredentials(true); // 是否允许发送Cookie
}
}
4.5 文件上传下载
文字讲解
文件上传和下载是Web应用的常见功能。Spring MVC通过MultipartFile接口简化了文件上传的处理。
代码示例
1. 文件上传
@RestController
@RequestMapping("/api/v1/files")
public class FileController {
private final Path rootLocation = Paths.get("upload-dir");
@PostMapping("/upload")
public ResponseEntity<String> handleFileUpload(@RequestParam("file") MultipartFile file) {
try {
if (file.isEmpty()) {
return ResponseEntity.badRequest().body("Failed to store empty file.");
}
// 确保上传目录存在
Files.createDirectories(rootLocation);
// 将文件保存到目标位置
Files.copy(file.getInputStream(), this.rootLocation.resolve(file.getOriginalFilename()));
return ResponseEntity.ok("File uploaded successfully: " + file.getOriginalFilename());
} catch (IOException e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Failed to store file.");
}
}
}
2. 文件下载
@GetMapping("/download/{filename:.+}")
public ResponseEntity<Resource> serveFile(@PathVariable String filename) {
try {
Path file = rootLocation.resolve(filename);
Resource resource = new UrlResource(file.toUri());
if (resource.exists() || resource.isReadable()) {
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
} else {
return ResponseEntity.notFound().build();
}
} catch (MalformedURLException e) {
return ResponseEntity.badRequest().build();
}
}
配置注意:对于大文件上传,可能需要在application.properties中配置相关属性:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
本章总结
本章我们全面学习了使用Spring Boot进行Web开发的基础知识。从Spring MVC的请求处理流程,到设计和实现RESTful API,再到处理验证、异常、CORS和文件操作等实际问题,我们已经具备了构建健壮Web服务的能力。这些技能是构建任何微服务应用的基石。
资源下载
本章的示例代码可以从以下链接下载:
【免费】微服务开发课程第四章的源码资源
424

被折叠的 条评论
为什么被折叠?



