第4章:Web开发基础

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请求处理流程
客户端 DispatcherServlet HandlerMapping @Controller HandlerAdapter HttpMessageConverter 发送HTTP请求 1. 查询Handler 返回HandlerExecutionChain 2. 调用HandlerAdapter (可选)转换请求体为Java对象 3. 调用Controller方法 返回ModelAndView或@ResponseBody对象 (可选)转换Java对象为响应体 返回处理结果 4. 响应客户端 客户端 DispatcherServlet HandlerMapping @Controller HandlerAdapter HttpMessageConverter

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-validatorspring-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服务的能力。这些技能是构建任何微服务应用的基石。

资源下载

本章的示例代码可以从以下链接下载:
【免费】微服务开发课程第四章的源码资源


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芝麻开门-新起点

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值