Spring MVC REST API設計詳解:從零構建高效接口

1. Spring MVC與REST API基礎

1.1 RESTful架構的六大約束詳解

RESTful架構是Roy Thomas Fielding在2000年博士論文中提出的軟件架構風格,它包含六個核心約束,這些約束共同構成了RESTful API的設計原則。

客戶端-服務器約束(Client-Server):將系統分為客戶端和服務器兩個獨立部分,客戶端負責用戶界面和交互,服務器負責數據存儲和業務邏輯 。這種分離使得客戶端和服務器可以獨立演進,提高系統的可維護性和可擴展性。

無狀態約束(Stateless):每個請求都必須包含理解該請求所需的所有信息,服務器不保存客戶端的狀態 。這種設計帶來了可見性、可靠性和可伸縮性的優勢,但也要求客戶端在每次請求中攜帶必要的身份驗證信息。

緩存約束(Cacheable):允許客戶端或中間層緩存服務器響應,減少重復請求,提高系統性能 。服務器需要明確標記響應是否可緩存,客戶端則需要正確處理緩存數據的有效性。

統一接口約束(Uniform Interface):這是RESTful架構的核心特征,要求接口遵循統一的標準 。具體包括:

  1. 資源標識:使用URI作為資源的唯一標識符
  2. 資源操作:通過HTTP方法對資源進行操作
  3. 自描述消息:每個消息包含足夠的信息來描述如何處理它
  4. 超媒體驅動應用狀態(HATEOAS):在響應中包含相關操作的鏈接

分層系統約束(Layered System):系統由多個層次組成,每個層次只與相鄰層次交互,這種設計提高了系統的可維護性和可擴展性 。

按需代碼約束(Code on Demand):允許服務器向客戶端發送可執行代碼,擴展客戶端功能 。雖然這個約束不是必須的,但在某些場景下可以簡化客戶端開發。

在實際開發中,我們通常關注Richardson成熟度模型,它將RESTful API分為四個級別:

  • Level 0:基于遠程過程調用(RPC)的Web服務
  • Level 1:將服務抽象為資源,每個資源由唯一的URI標識
  • Level 2:使用HTTP方法(GET、POST、PUT、DELETE)對資源進行操作
  • Level 3:實現HATEOAS,客戶端通過超媒體鏈接發現下一步操作

在Spring MVC中構建RESTful API,我們通常至少達到Level 2級別,即使用HTTP方法對資源進行操作,但可能不完全實現HATEOAS。

1.2 Spring MVC框架核心組件與工作流程

Spring MVC是一個基于Java的Web框架,實現了MVC(模型-視圖-控制器)設計模式

8

。它通過分離業務邏輯、數據和界面顯示來簡化Web應用開發。

核心組件

  • DispatcherServlet:前端控制器,負責接收HTTP請求并分發給相應的處理器
  • HandlerMapping:處理器映射,負責將請求映射到對應的控制器方法
  • Controller:控制器,處理請求并返回處理結果
  • Model:模型,封裝請求處理過程中產生的數據
  • View:視圖,負責將模型數據渲染成特定格式的響應
  • ViewResolver:視圖解析器,負責解析視圖名稱并返回視圖對象

工作流程

  1. 客戶端發送HTTP請求到服務器
  2. 請求由Servlet容器(如Tomcat)接收并傳遞給DispatcherServlet
  3. DispatcherServlet查詢HandlerMapping,找到處理該請求的Controller
  4. Controller處理請求,生成Model數據
  5. DispatcherServlet將Model和視圖名稱傳遞給ViewResolver
  6. ViewResolver解析視圖名稱,找到對應的View
  7. View使用Model數據渲染響應,返回給客戶端

在構建RESTful API時,我們通常使用@RestController代替傳統的@Controller和 JSP/Thymeleaf視圖,這樣可以將響應直接以JSON格式返回,無需視圖解析器。

1.3 Spring MVC與Spring WebFlux對比分析

Spring MVC和Spring WebFlux都是Spring框架提供的Web開發解決方案,但它們在底層實現和適用場景上有顯著差異。

特性Spring MVCSpring WebFlux
編程模型命令式編程響應式編程
線程模型阻塞式IO,每個請求一個線程非阻塞式IO,使用事件循環和背壓機制
適用場景傳統Web應用,高并發但非實時場景高并發、實時性要求高的場景,如IoT、實時監控
性能依賴線程池,線程數受限更高的資源利用率,支持百萬級并發連接
依賴Servlet APIReactor或RxJava

Spring MVC是基于Servlet API的傳統框架,采用命令式編程方式,每個請求對應一個線程 。它適合大多數企業級應用開發,尤其是需要與大量遺留系統集成的場景。

Spring WebFlux是Spring 5引入的響應式框架,基于Reactor庫實現 。它采用函數式編程方式,能夠處理非阻塞IO操作,適合高并發、實時性要求高的場景。

在REST API設計中,Spring MVC提供了更為簡潔和直觀的實現方式,而Spring WebFlux則提供了更好的并發性能。對于大多數企業級應用,Spring MVC已經足夠;而對于需要處理大量并發連接的場景,Spring WebFlux可能是更好的選擇。

2. REST API設計核心流程

2.1 資源建模與路徑設計原則

資源建模是REST API設計的第一步,它要求我們將業務實體抽象為資源

12

。在Spring MVC中,通常使用實體類(如User、Product)來表示資源。

路徑設計原則

  • 使用名詞復數形式表示資源集合(如/users)
  • 使用單數形式表示單個資源(如/users/1)
  • 避免使用動詞(如/getUsers),而是通過HTTP方法表示操作
  • 使用連字符增加可讀性(如/user-profile)
  • 使用層級關系表示資源關系(如/users/1/orders)

在Spring MVC中,我們可以使用@RequestMapping注解來定義資源路徑:

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@GetMappingpublic ResponseEntity<List<User>> AllUsers() {// 返回所有用戶}@GetMapping("{/id}")public ResponseEntity<User> getUserById(@PathVariable Long id) {// 返回指定ID的用戶}
}

資源建模的關鍵點

  • 每個資源應該有明確的邊界和職責
  • 資源之間可以通過鏈接(HATEOAS)建立關系
  • 路徑設計應該簡潔、直觀,易于理解
2.2 HTTP方法與狀態碼的語義化使用

HTTP方法是RESTful API中操作資源的核心方式,它們具有特定的語義和約束

12

  • GET:獲取資源,應該是安全的(不修改資源狀態)和冪等的
  • POST:創建資源,非冪等的
  • PUT:全量更新資源,冪等的
  • DELETE:刪除資源,冪等的
  • PATCH:部分更新資源,冪等的(如果正確實現)
  • HEAD:獲取資源元數據,與GET類似但不返回內容

在Spring MVC中,我們可以使用以下注解來映射HTTP方法

3

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@GetMappingpublic ResponseEntity<List<User>> AllUsers() {// 獲取所有用戶}@PostMappingpublic ResponseEntity<User> createUser(@RequestBody User user) {// 創建新用戶}@PutMapping("{/id}")public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {// 全量更新用戶}@DeleteMapping("{/id}")public ResponseEntity<?>.deleteUser(@PathVariable Long id) {// 刪除用戶}@PatchMapping("{/id}")public ResponseEntity<User> patchUser(@PathVariable Long id, @RequestBody UserPatch patch) {// 部分更新用戶}
}

HTTP狀態碼用于表示請求的處理結果

12

  • 200 OK:請求成功,返回資源
  • 201 Created:資源創建成功,包含新資源的URI
  • 204 No Content:請求成功,但沒有返回內容
  • 400 Bad Request:請求參數錯誤
  • 401 Unauthorized:用戶未認證
  • 403 Forbidden:用戶無權限
  • 404 Not Found:資源不存在
  • 500 Internal Server Error:服務器內部錯誤

在Spring MVC中,我們可以使用ResponseEntity來返回帶有狀態碼的響應

3

java

深色版本

@GetMapping("{/id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {User user = userService.getUser(id);if (user == null) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(user);
}

HTTP方法與狀態碼的正確使用是RESTful API設計的基礎,它們幫助客戶端理解資源的操作方式和結果。

2.3 參數綁定與數據傳輸對象設計

參數綁定是REST API處理請求數據的關鍵步驟,在Spring MVC中我們可以通過多種注解來實現:

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {// 路徑變量參數綁定@GetMapping("{/id}")public ResponseEntity<User> getUser(@PathVariable Long id) {// ...}// 查詢參數綁定@GetMappingpublic ResponseEntity<List<User>> getUsers(@RequestParam(required = false, defaultValue = "0") Integer page,@RequestParam(required = false, defaultValue = "10") Integer size,@RequestParam(required = false) String name) {// ...}// 請求體參數綁定@PostMappingpublic ResponseEntity<User> createUser(@RequestBody UserDTO userDTO) {// ...}
}

數據傳輸對象(DTO)設計是REST API的重要環節,它幫助我們解耦業務實體和API響應

16

java

深色版本

@Data
public class UserDTO {private Long id;private String name;private String email;private String role;// 省略getter和setter
}@Service
public class UserService {public UserDTO getUserDTO(Long id) {User user = userRepository.findById(id).orElse(null);if (user == null) {return null;}return modelMapper.map(user, UserDTO.class);}
}

參數綁定的最佳實踐

  • 使用路徑變量(@PathVariable)表示資源標識符
  • 使用查詢參數(@RequestParam)表示過濾條件、分頁參數等
  • 使用請求體(@RequestBody)傳遞復雜數據
  • 使用DTO解耦業務實體和API響應,避免暴露內部細節
  • 使用參數校驗注解(@Valid)確保輸入數據的有效性
2.4 響應格式化與多格式支持配置

響應格式化是REST API設計的重要環節,它決定了客戶端如何接收和處理數據

15

。在Spring MVC中,我們可以使用以下方式實現:

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@GetMapping(produces = "application/json")public ResponseEntity<List<User>> AllUsers() {// 返回JSON格式}@GetMapping(produces = "application/xml")public ResponseEntity<List<User>> AllUsersXML() {// 返回XML格式}
}

多格式支持是RESTful API的重要特性,它允許客戶端指定期望的數據格式

11

。在Spring Boot中,我們可以使用ContentNegotiationManager來配置多格式支持:

java

深色版本

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void configureContentNegotiation ContentNegotiationManagerBuilder builder) {builder.defaultContentNegotiationStrategy(ContentNegotiationManagerBuilder七大默認策略);}
}

響應格式化的最佳實踐

  • 使用JSON作為主要數據格式,因為它輕量且易于解析
  • 支持多種內容類型(如JSON、XML),通過Accept頭協商
  • 使用標準的HTTP狀態碼表示請求結果
  • 對于錯誤響應,返回包含錯誤信息和代碼的標準格式?

    7

  • 使用自定義響應對象統一API響應格式

3. 關鍵注解與配置詳解

3.1 @RestController與請求映射注解

@RestController是Spring MVC中構建RESTful API的核心注解,它相當于@Controller和@ResponseBody的組合

3

java

深色版本

@RestController
public class UserController {// 所有方法返回值都會被轉換為響應體,無需額外使用@ResponseBody
}

請求映射注解用于將HTTP請求映射到控制器方法

3

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@GetMappingpublic ResponseEntity<List<User>> AllUsers() {// ...}@PostMappingpublic ResponseEntity<User> createUser(@RequestBody User user) {// ...}@PutMapping("{/id}")public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {// ...}@DeleteMapping("{/id}")public ResponseEntity<?>.deleteUser(@PathVariable Long id) {// ...}@PatchMapping("{/id}")public ResponseEntity<User> patchUser(@PathVariable Long id, @RequestBody UserPatch patch) {// ...}
}

請求映射注解的高級用法

  • 使用consumes指定請求內容類型
  • 使用produces指定響應內容類型
  • 使用method指定允許的HTTP方法
  • 使用params指定請求參數條件

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@PostMapping(consumes = "application/json",produces = "application/json",method = RequestMethod.POST,params = "action=register")public ResponseEntity<User> createUser(@RequestBody User user) {// ...}
}

關鍵配置

  • 在Spring Boot中,無需額外配置就可以使用這些注解
  • 在傳統Spring MVC中,需要配置HandlerMapping和HandlerAdapter
3.2 @RequestBody與@PathVariable的深度解析

@RequestBody用于將HTTP請求體轉換為Java對象

4

java

深色版本

@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody UserDTO userDTO) {User user = modelMapper.map(userDTO, User.class);User savedUser = userService.save(user);return ResponseEntity created(URI.create("/users/" + savedUser.getId())).body(savedUser);
}

@PathVariable用于從URL路徑中提取變量值

4

java

深色版本

@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {User user = userService.getUser(id);if (user == null) {return ResponseEntity.notFound().build();}return ResponseEntity.ok(user);
}

@RequestBody的配置

  • 默認使用Jackson庫處理JSON轉換
  • 可以通過配置改變轉換器
  • 需要處理可能的轉換異常

java

深色版本

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {// 添加或修改消息轉換器}
}

@PathVariable的使用場景

  • 表示資源標識符(如/users/123)
  • 表示資源層級關系(如/users/123/orders)
  • 表示資源篩選條件(如/users?status=active)

關鍵配置

  • 在Spring Boot中,無需額外配置就可以使用這些注解
  • 在傳統Spring MVC中,需要配置HandlerMapping和HandlerAdapter
3.3 異常處理注解與全局異常處理器

異常處理是REST API設計的重要環節,它決定了API如何處理錯誤情況

22

。在Spring MVC中,我們可以使用以下方式實現:

java

深色版本

@RestController
@RequestMapping("/users")
public class UserController {@GetMapping("{/id}")public ResponseEntity<User> getUser(@PathVariable Long id) {User user = userService.getUser(id);if (user == null) {throw new UserNotFoundException("用戶不存在");}return ResponseEntity.ok(user);}
}

全局異常處理器用于統一處理API中的異常

22

java

深色版本

@RestControllerAdvice
public class GlobalExceptionHandler {@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<Error> handleUserNotFound(UserNotFoundException ex) {Error error = new Error();error碼 = 404;error消息 = ex.getMessage();return ResponseEntity.status(HttpStatus NOT_FOUND).body(error);}@ExceptionHandler(AssertionError.class)public ResponseEntity<Error> handleAssertionError(AssertionError ex) {Error error = new Error();error碼 = 500;error消息 = ex.getMessage();return ResponseEntity.status(HttpStatus BAD_REQUEST).body(error);}
}

自定義異常用于表示特定的業務錯誤

22

java

深色版本

public class UserNotFoundException extends RuntimeException {public UserNotFoundException(String message) {super(message);}
}

異常處理的最佳實踐

  • 使用自定義異常表示業務錯誤
  • 使用全局異常處理器統一處理異常
  • 返回包含錯誤信息和代碼的標準格式
  • 根據異常類型返回合適的HTTP狀態碼
  • 避免暴露敏感信息和內部錯誤細節
3.4 跨域請求處理與安全配置

跨域請求處理(CORS)是REST API設計中常見的需求,在Spring MVC中可以通過以下方式實現:

java

深色版本

@RestController
@RequestMapping("/users")
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {// ...
}

全局CORS配置用于統一處理所有API的跨域請求

17

java

深色版本

@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("http://localhost:3000", "https://example.com").allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH").allowedHeaders("*").exposedHeaders("Authorization").allowCredentials(true).maxAge(3600);}
}

安全配置是REST API設計的重要環節,在Spring Security中可以通過以下方式實現:

java

深色版本

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/users/**").permitAll().antMatchers("/admin/**").hasRole("ADMIN").anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().csrf().disable(); // 關閉CSRF防護}@Autowiredpublic void configureGlobal plasure.antMatchers("/users/**").permitAll().antMatchers("/admin/**").hasRole("ADMIN").anyRequest().authenticated().and().formLogin().loginPage("/login").permitAll().and().csrf().disable(); // 關閉CSRF防護}@Autowiredpublic void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("user").password("user").roles("USER").and().withUser("admin").password("admin").roles("ADMIN");}
}

安全配置的最佳實踐

  • 使用Spring Security進行身份驗證和授權
  • 根據需要啟用或禁用CSRF防護
  • 使用HTTPS保護數據傳輸
  • 使用JWT進行無狀態認證
  • 使用OAuth2進行第三方認證

4. 實戰案例:用戶管理API開發

4.1 用戶實體類與DTO設計

用戶實體類(User)表示業務層的用戶數據:

java

深色版本

@Entity
@Table(name = "users")
@Data
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(length = 50, nullable = false)@NotBlank(message = "用戶名不能為空")@Size(min = 3, max = 50, message = "用戶名長度應在3-50之間")private String username;@Column(length = 100, nullable = false)@NotBlank(message = "密碼不能為空")@Size(min = 8, max = 100, message = "密碼長度應在8-100之間")private String password;@Column(length = 100, nullable = false)@Email(message = "郵箱格式不正確")private String email;@Enumerated(EnumType.STRING)@Column(length = 20)private Role role = Role USER;@Column(length = 20)@Enumerated(EnumType.STRING)private Status status = Status active;@Column@Temporal(TemporalType.TIMESTAMP)private Date createTime;@Column@Temporal(TemporalType.TIMESTAMP)private Date last登陸時間;
}

用戶DTO(UserDTO)表示API響應的數據:

java

深色版本

@Data
public class UserDTO {private Long id;private String username;private String email;private String role;private String status;private String createTime;private String last登陸時間;// 轉換方法public static UserDTO fromUser(User user) {UserDTO userDTO = new UserDTO();userDTO.id = user.getId();userDTO.username = user username();userDTO.email = user email();userDTO.role = user.getRole().name();userDTO.status = user.getStatus().name();userDTO.createTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(user.getCreateTime());userDTO.last登陸時間 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(user LastLoginTime());return userDTO;}
}

角色枚舉(Role)表示用戶角色:

java

深色版本

public enum Role {USER,ADMIN,MODERATOR
}

狀態枚舉(Status)表示用戶狀態:

java

深色版本

public enum Status {活躍,不活躍,已刪除
}

實體類與DTO設計的關鍵點

  • 實體類包含完整的業務邏輯和約束
  • DTO只包含需要暴露給客戶端的字段
  • 使用轉換方法(如fromUser)將實體轉換為DTO
  • 使用枚舉表示狀態和角色,提高代碼可讀性
4.2 基礎CRUD接口實現

創建用戶接口(POST /users):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@PostMappingpublic ResponseEntity<UserDTO> createUser(@RequestBody UserCreateDTO userCreateDTO) {// 參數校驗if (userCreateDTO == null || Strings.isEmpty(userCreateDTO username())) {return ResponseEntity badRequest().body(new Error("用戶名不能為空"));}// 轉換為實體User user = UserCreateDTO.toUser(userCreateDTO);// 保存用戶User savedUser = userService.save(user);// 返回創建的用戶UserDTO userDTO = UserDTO.fromUser(savedUser);return ResponseEntity created(URI.create("/api/v1/users/" + savedUser.getId())).body(userDTO);}
}

獲取用戶列表接口(GET /users):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@GetMappingpublic ResponseEntity<List<UserDTO>> AllUsers() {List<User> users = userService AllUsers();List<UserDTO> userDTOS = users.stream().map(UserDTO::fromUser).collect(Collectors.toList());return ResponseEntity ok(userDTOS);}
}

獲取單個用戶接口(GET /users/{id}):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@GetMapping("{/id}")public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {User user = userService.getUser(id);if (user == null) {return ResponseEntity notFound().build();}UserDTO userDTO = UserDTO.fromUser(user);return ResponseEntity ok(userDTO);}
}

更新用戶接口(PUT /users/{id}):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@PutMapping("{/id}")public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody UserUpdateDTO userUpdateDTO) {User existingUser = userService.getUser(id);if (existingUser == null) {return ResponseEntity notFound().build();}// 轉換為實體User updatedUser = UserUpdateDTO.toUser(existingUser, userUpdateDTO);// 保存用戶User savedUser = userService.save(updatedUser);// 返回更新后的用戶UserDTO userDTO = UserDTO.fromUser(savedUser);return ResponseEntity ok(userDTO);}
}

刪除用戶接口(DELETE /users/{id}):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@DeleteMapping("{/id}")public ResponseEntity<?>.deleteUser(@PathVariable Long id) {User user = userService.getUser(id);if (user == null) {return ResponseEntity notFound().build();}userService.delete(user);return ResponseEntity noContent().build();}
}

基礎CRUD接口的關鍵點

  • 使用標準的HTTP方法表示操作(GET、POST、PUT、DELETE)
  • 使用標準的HTTP狀態碼表示結果(200、201、204、404、400)
  • 使用DTO解耦業務實體和API響應
  • 使用ResponseEntity返回帶有狀態碼的響應
  • 使用參數校驗確保輸入數據的有效性
4.3 分頁與復雜查詢接口

分頁接口(GET /users?page=0&size=10):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@GetMappingpublic ResponseEntity<Page<UserDTO>>頁碼, @RequestParam(required = false, defaultValue = "10") Integer大小,@RequestParam(required = false) String name,@RequestParam(required = false) String email,@RequestParam(required = false) String role,@RequestParam(required = false) String status) {// 構建分頁參數Pageable pageable = PageRequest ofPage(page, size);// 構建查詢條件Example<User> example = Example of(User.class).with ignoreNulls().where(name != null ? Example where username(). containing(name) : null,email != null ? Example where email(). containing(email) : null,role != null ? Example where role(). eq(Role.valueOf(role)) : null,status != null ? Example where status(). eq(Status.valueOf(status)) : null);// 查詢用戶Page<User> users = userService AllUsers(example, pageable);// 轉換為DTOPage<UserDTO> userDTOS = users.map(UserDTO::fromUser);return ResponseEntity ok(userDTOS);}
}

復雜查詢接口(GET /users?sort=name&order=asc):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@Autowiredprivate UserService userService;@GetMappingpublic ResponseEntity<List<UserDTO>>查詢(@RequestParam(required = false) String name,@RequestParam(required = false) String email,@RequestParam(required = false) String role,@RequestParam(required = false) String status,@RequestParam(required = false) String sort,@RequestParam(required = false) String order) {// 構建查詢條件Example<User> example = Example of(User.class).with ignoreNulls().where(name != null ? Example where username(). containing(name) : null,email != null ? Example where email(). containing(email) : null,role != null ? Example where role(). eq(Role.valueOf(role)) : null,status != null ? Example where status(). eq(Status.valueOf(status)) : null);// 構建排序條件Sort sort = Sort by((sort != null && order != null) ?new Sort(Sort Order.valueOf(order), sort) : null);// 查詢用戶List<User> users = userService AllUsers(example, sort);// 轉換為DTOList<UserDTO> userDTOS = users.stream().map(UserDTO::fromUser).collect(Collectors.toList());return ResponseEntity ok(userDTOS);}
}

分頁與復雜查詢的關鍵點

  • 使用Pageable參數處理分頁
  • 使用Example參數處理復雜查詢條件
  • 使用Sort參數處理排序
  • 使用參數校驗確保輸入數據的有效性
  • 返回包含分頁信息的響應
4.4 全局異常處理與自定義錯誤響應

全局異常處理器(GlobalExceptionHandler)統一處理API異常

22

java

深色版本

@RestControllerAdvice
public class GlobalExceptionHandler {// 處理用戶不存在異常@ExceptionHandler(UserNotFoundException.class)public ResponseEntity<Error> handleUserNotFound(UserNotFoundException ex) {return createErrorEntity(HttpStatus NOT_FOUND, ex.getMessage());}// 處理參數校驗異常@ExceptionHandler(MethodArgumentNotValidException.class)public ResponseEntity<Error> handleValidationException(MethodArgumentNotValidException ex) {String message = ex.getBindingResult(). All errors(). get(0).DEFAULT Message();return createErrorEntity(HttpStatus BAD_REQUEST, message);}// 處理一般異常@ExceptionHandler(Exception.class)public ResponseEntity<Error> handleGeneralException(Exception ex) {return createErrorEntity(HttpStatus INTERNAL_SERVER_ERROR, "服務器內部錯誤");}// 創建錯誤響應實體private ResponseEntity<Error> createErrorEntity(HttpStatus status, String message) {Error error = new Error();error碼 = status.value();error消息 = message;return ResponseEntity.status(status).body(error);}
}

自定義錯誤對象(Error)表示API錯誤信息

13

java

深色版本

@Data
public class Error {private Integer code; // 錯誤碼private String message; // 錯誤信息private String timestamp; // 時間戳private String path; // 請求路徑// 構造方法public Error() {this.timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());}public Error(Integer code, String message) {this();this.code = code;this.message = message;}
}

全局異常處理的關鍵點

  • 使用@RestControllerAdvice注解聲明全局異常處理器
  • 使用@ExceptionHandler注解處理特定異常
  • 返回統一的錯誤格式,包含錯誤碼和信息
  • 避免暴露敏感信息和內部錯誤細節
  • 根據異常類型返回合適的HTTP狀態碼
4.5 API版本控制策略實現

URI版本控制(/api/v1/users):

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserControllerV1 {// v1版本的接口實現
}@RestController
@RequestMapping("/api/v2/users")
public class UserControllerV2 {// v2版本的接口實現
}

請求頭版本控制(X-API-Version: v1):

java

深色版本

// 自定義版本條件
public class VersionCondition implements RequestCondition<VersionCondition> {private final String version;public VersionCondition(String version) {this.version = version;}@Overridepublic VersionCondition combine(VersionCondition other) {return new VersionCondition(other.version);}@Overridepublic VersionCondition getMatchingCondition(HttpServletRequest request) {String requestedVersion = request.getHeader("X-API-Version");if (version.equals(requestedVersion)) {return this;}return null;}@Overridepublic int.compareTo(VersionCondition other, HttpServletRequest request) {return this.version.compareTo(other.version);}
}// 自定義版本注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interfaceApiVersion {String value() default "v1";
}// 自定義版本映射
public class VersionRequestMappingHandlerMapping extends RequestMappingHandlerMapping {@Overrideprotected RequestMappingInfo getMappingForMethod(Method method, Class<?> declaringClass) {RequestMappingInfo info = super.getMappingForMethod(method, declaringClass);if (info != null) {info = info.combinedWith(new VersionCondition(method.getAnnotation(ApiVersion.class) != null ?method.getAnnotation(ApiVersion.class).value() :declaringClass.getAnnotation(ApiVersion.class) != null ?declaringClass.getAnnotation(ApiVersion.class).value() :"v1"));}return info;}
}// 注冊自定義版本映射
@Configuration
public class WebConfig implements WebMvcConfigurer {@Beanpublic RequestMappingHandlerMapping requestMappingHandlerMapping() {return new VersionRequestMappingHandlerMapping();}
}// 使用版本注解的控制器
@RestController
@RequestMapping("/api/users")
@ApiVersion("v1")
public class UserControllerV1 {// v1版本的接口實現
}@RestController
@RequestMapping("/api/users")
@ApiVersion("v2")
public class UserControllerV2 {// v2版本的接口實現
}

API版本控制的關鍵點

  • 使用URI路徑或請求頭標識API版本
  • 使用自定義注解和條件匹配實現版本控制
  • 全局配置版本映射處理器
  • 不同版本的接口可以共存,客戶端通過版本標識選擇
  • 版本控制策略應該簡單明了,易于理解

5. 設計最佳實踐與常見問題

5.1 參數校驗與自定義校驗規則

參數校驗是REST API設計的重要環節,它確保客戶端傳遞的參數符合預期

7

java

深色版本

@RestController
@RequestMapping("/api/v1/users")
public class UserController {@PostMappingpublic ResponseEntity<UserDTO> createUser(@Valid @RequestBody UserCreateDTO userCreateDTO) {// ...}
}// 參數校驗DTO
@Data
public class UserCreateDTO {@NotBlank(message = "用戶名不能為空")@Size(min = 3, max = 50, message = "用戶名長度應在3-50之間")private String username;@NotBlank(message = "密碼不能為空")@Size(min = 8, max = 100, message = "密碼長度應在8-100之間")private String password;@Email(message = "郵箱格式不正確")private String email;@Enumerated(EnumType STRING)@Column(length = 20)private Role role = Role USER;// 轉換方法public static User toUser(UserCreateDTO userCreateDTO) {User user = new User();user.setUsername(userCreateDTO username());user.setPassword(userCreateDTO password());user.setEmail(userCreateDTO email());user.setRole(userCreateDTO.getRole());return user;}
}

自定義校驗規則(CustomValidator)用于處理更復雜的校驗邏輯

7

java

深色版本

@Constraint(validatedBy = UniqueEmailValidator.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface UniqueEmail {String message() default "郵箱已存在";Class<?>[] groups() default {};Class<? extends Payload>[] payload() default {};
}// 自定義校驗器
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {@Autowiredprivate UserService userService;@Overridepublic void initialize(UniqueEmail constraintAnnotation) {// 初始化}@Overridepublic boolean isValid(String email, ConstraintValidatorContext context) {if (email == null || email.trim().isEmpty()) {return true;}return userService.checkEmail Unique(email);}
}// 使用自定義校驗注解
@Data
public class UserCreateDTO {@NotBlank(message = "用戶名不能為空")@Size(min = 3, max = 50, message = "用戶名長度應在3-50之間")private String username;@NotBlank(message = "密碼不能為空")@Size(min = 8, max = 100, message = "密碼長度應在8-100之間")private String password;@Email(message = "郵箱格式不正確")@UniqueEmailprivate String email;@Enumerated(EnumType STRING)@Column(length = 20)private Role role = Role USER;// 轉換方法public static User toUser(UserCreateDTO userCreateDTO) {User user = new User();user.setUsername(userCreateDTO username());user.setPassword(userCreateDTO password());user plemail(userCreateDTO email());user.setRole(userCreateDTO.getRole());return user;}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/92507.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/92507.shtml
英文地址,請注明出處:http://en.pswp.cn/web/92507.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于STM32F030C8T6單片機實現與CH224Q誘騙芯片的I2C通信和電壓輸出配置

基于項目的需要,對STM32F030的IIC研究了幾天,終于完成了通信,接下來具體實現如下: 本單片機使用的是PB8和PB9管腳進行實現,采用的是模擬的IIC進行 void MyI2C_W_SCL(uint8_t BitValue)//這三個函數將讀寫io口封裝起來,增強可讀性 { GPIO_WriteBit(GPIOB, GPIO_Pin_8…

TSMaster-C小程序使用

打開同星的TSMaster&#xff0c;推薦用32版本的&#xff0c;比64更穩定。同星的TSMaster的C小程序支持用戶嵌入代碼來控制CAN報文的收發邏輯。便于開發。點擊設計里面的C小程序。 比如我現在想用小程序來實現繼電器0先開后關開1s關1s&#xff0c;然后繼電器1開1s關1s…如此往復…

XSS滲透測試原理/步驟/攻擊方法/防御/常用語法

**核心概念回顧&#xff1a;**XSS漏洞一直被評估為web漏洞中危害較大的漏洞&#xff0c;在OWASP TOP10的排名中一直屬于前三的江湖地位。XSS是一種發生在前端瀏覽器端的漏洞&#xff0c;所以其危害的對象也是前端用戶。 形成XSS漏洞的主要原因是程序對輸入和輸出沒有做合適的處…

目標檢測數據集 - 自動駕駛場景道路異常檢測數據集下載「包含VOC、COCO、YOLO三種格式」

數據集介紹&#xff1a;自動駕駛場景道路異常檢測數據集&#xff0c;真實場景高質量道路圖片數據&#xff0c;涉及場景豐富&#xff0c;且類別豐富&#xff0c;劃分為 "LMVs 輕型機動車&#xff08;汽車、摩托車、小型卡車、小型貨車"、"HMVs 公交車、卡車、拖拉…

多模態新方向|從數據融合到場景落地,解鎖視覺感知新范式

來gongzhonghao【圖靈學術計算機論文輔導】&#xff0c;快速拿捏更多計算機SCI/CCF發文資訊&#xff5e;多模態學習&#xff08;Multimodal Learning&#xff09;是通過整合多種數據模態來提升模型對復雜場景感知與理解能力的技術&#xff0c;其核心是利用不同模態的互補性突破…

機器學習之隨機森林

目錄 一、什么是隨機森林&#xff1f; 1. 從決策樹到集成學習&#xff1a;為什么需要 "森林"&#xff1f; 2.什么是集成學習 二、隨機森林的工作原理 三、隨機森林構造過程 四、隨機森林api介紹 五、隨機森林的優缺點 六、垃圾郵件判斷案例 1.數據集介紹 ?…

云平臺運維工具 —— 阿里云原生工具

一、簡介阿里云作為國內領先的云服務提供商&#xff0c;擁有一套完整的原生運維工具體系&#xff0c;這些工具與阿里云的各類服務深度融合&#xff0c;能夠滿足用戶在資源部署、監控告警、權限管理、自動化運維等方面的需求。無論是簡單的應用托管還是復雜的企業級架構&#xf…

Linux-Day10.系統安全保護web服務管理

今日目標&#xff1a;- 日志管理- 系統安全保護 SELinux&#xff08;重點&#xff09;- 構建基本web服務&#xff08;重點&#xff09;環境準備還原快照網絡配置完成&#xff0c;開啟虛擬機A與虛擬機B用真機連通虛擬機去操作&#xff0c;準本好Xshell一、常用的網絡工具ip命令1…

解決:開啟魔法后vscode pip命令不能安裝中科大python鏡像問題

閑言少敘&#xff0c;最終實現效果就是在開啟魔法情況下&#xff0c;vscode命令行任何能通過中科大python鏡像安裝第三方庫&#xff0c;又快又不消耗魔法流量。簡單來說就兩步&#x1f447;&#xff1a; 第一步&#xff1a;配置 pip.ini 中的代理 找到或創建 pip.ini 文件&…

優化Google Pubsub到GCS的文件整合策略

引言 在使用Google Cloud Platform (GCP) 的Pubsub服務時,我們常常會遇到將消息存儲到Google Cloud Storage (GCS) 作為Avro文件的問題。本文將深入探討如何優化Google Pubsub到GCS的文件整合策略,以避免每個消息都單獨生成一個Avro文件,達到將多個消息整合到一個文件的目的…

基于鐵頭山羊STM32的平衡車電機轉速開環閉環matlab仿真

基于鐵頭山羊STM32的平衡車電機轉速開環閉環matlab仿真前言一、電機開環傳遞函數1.1 電機開環傳遞函數的零極點1.2 求系統的參數和繪制波特圖二、增加PI控制器后系統開環傳遞函數三、電機系統閉環傳遞函數四、simulink仿真五、幅值裕度、相位裕度、相位穿越頻率和截止頻率&…

P1044 [NOIP 2003 普及組] 棧

P1044 [NOIP 2003 普及組] 棧 - 洛谷 題解來自洛谷題解&#xff0c;做筆記用 假設用一個函數來表示&#xff1a; x表示當前還未入棧的數字個數 y表示當前棧中的數字個數 orz&#xff0c;大佬們真的是很厲害&#xff0c;想著遞推但是只拿了60分 #include <bits/stdc.h&g…

linux mysql 8.X主從復制

準備兩臺linux服務器,注意要鎖ip我這里如上圖 主庫 192.168.5.5/24 從庫 192.168.5.10/24 接下來確定mysql是否啟動成功并且能從外部連接 主庫從庫主服務器配置 vim編輯主服務器配置 vim /etc/my.cnf注意是下面那個添加配置代碼 log-binmysql-bin # 配置二進制日志 server-id1…

豆包新模型矩陣+PromptPilot:AI開發效率革命的終極方案

> **一套讓AI開發者告別“調參煉獄”的黃金組合,效率提升300%的實戰指南** ## 一、AI開發的范式轉移:從通用模型到**場景化矩陣** 2025年,AI應用開發面臨核心矛盾:**業務場景高度細分**與**模型能力同質化**的沖突。火山引擎的破局之道是推出**豆包1.6模型矩陣**——三…

瑞利雜波背景下不同環境的虛警概率與目標檢測概率仿真

仿真方案&#xff0c;研究在瑞利雜波背景下&#xff0c;均勻環境、多目標環境和雜波墻環境中的虛警概率(Pfa)和目標檢測概率(Pd)。 理論基礎 瑞利分布 瑞利分布常用于描述雷達雜波的幅度分布&#xff1a; p(x) (x/σ) * exp(-x/(2σ)), x ≥ 0其中σ是尺度參數&#xff0c;決定…

Spring Boot + Tesseract異步處理框架深度解析,OCR發票識別流水線

Spring Boot Tesseract異步處理框架深度解析&#xff0c;OCR發票識別流水線一、系統架構設計1.1 分布式流水線架構1.2 核心組件職責1.3 數據流設計二、Spring Boot異步框架實現2.1 線程池優化配置2.2 異步服務層設計2.3 異步流水線編排三、Tesseract深度優化3.1 發票專用訓練模…

Arm Qt編譯Qt例程出錯 GLES3/gl3.h: No such file or directory

解決方法 PC&#xff1a;Ubuntu22.04.1 QtCreator&#xff1a; 4.11.1 交叉編譯環境&#xff1a;YC6254 開發板提供的 5-編譯工具鏈->qt交叉編譯工具 在之前博客配置成功的交叉編譯環境&#xff0c;編譯Qt5.14.8自帶部分Example時&#xff0c;出現 GLES3/gl3.h: No such …

HydroOJ:開源在線判題系統的創新與實踐

HydroOJ&#xff1a;開源在線判題系統的創新與實踐 在數字化與信息化深度融合的今天&#xff0c;編程教育已成為全球教育改革的重要方向&#xff0c;而在線判題系統&#xff08;Online Judge&#xff0c;簡稱 OJ&#xff09;作為編程學習、算法訓練和競賽組織的核心工具&#…

tcpdump問題記錄

問題一: scapy發送vlan報文&#xff0c;tcpdump過濾抓包未抓到包的問題 發包 sendp([Ether(src"11:22:33:44:55:00")/Dot1Q(vlan1001)/IP()/UDP()/"Hello, VLAN!"], iface"ens9")vlan過濾抓包&#xff0c;不OK。 # tcpdump -i ens9 -nnvve -Q ou…

計算機視覺面試保溫:CLIP(對比語言-圖像預訓練)和BERT技術概述

一、CLIP技術 CLIP&#xff0c;全稱 Contrastive Language-Image Pre-training&#xff08;對比語言-圖像預訓練&#xff09;&#xff0c;是由 OpenAI 在 2021 年提出的一個里程碑式的模型。它的核心思想在于利用自然語言作為監督信號來學習強大的視覺表示&#xff0c;從而打破…