快学快用系列:一文学会java后端WebApi开发

快学快用系列:一文学会java后端WebApi开发
在这里插入图片描述

文章目录

在这里插入图片描述

第一部分:Web API开发基础概念

1.1 什么是Web API

Web API(Application Programming Interface)是一种允许不同软件系统之间进行通信的接口。在Web开发中,API通常基于HTTP协议,使用RESTful架构风格,通过URL端点提供数据和服务。

Web API的核心特点:

  • 基于HTTP/HTTPS协议
  • 返回结构化数据(JSON/XML)
  • 无状态通信
  • 跨平台兼容

1.2 RESTful API设计原则

REST(Representational State Transfer)是一种软件架构风格,包含以下核心原则:

  1. 统一接口:使用标准的HTTP方法和状态码
  2. 无状态:每个请求包含所有必要信息
  3. 可缓存:响应应标记为可缓存或不可缓存
  4. 分层系统:客户端不需要知道是否连接到最终服务器
  5. 按需代码:服务器可以临时扩展功能

第二部分:开发环境搭建

2.1 环境要求

必需工具:

  • JDK 8或以上版本
  • IDE(IntelliJ IDEA/Eclipse)
  • Maven 3.6+ 或 Gradle
  • MySQL/PostgreSQL数据库

2.2 创建Spring Boot项目

使用Spring Initializr创建项目:

<!-- pom.xml --><?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.0</version><relativePath/></parent><groupId>com.example</groupId><artifactId>webapi-demo</artifactId><version>1.0.0</version><properties><java.version>11</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies></project>

2.3 配置文件

# application.ymlserver:port:8080servlet:context-path: /api spring:datasource:url: jdbc:mysql://localhost:3306/webapi_db username: root password: password driver-class-name: com.mysql.cj.jdbc.Driver jpa:hibernate:ddl-auto: update show-sql:trueproperties:hibernate:dialect: org.hibernate.dialect.MySQL8Dialect format_sql:truelogging:level:com.example: DEBUG org.hibernate.SQL: DEBUG 

第三部分:项目架构设计

3.1 分层架构

典型的Java Web API采用分层架构:

Controller层 (API接口) ↓ Service层 (业务逻辑) ↓ Repository层 (数据访问) ↓ Model层 (数据模型) 

3.2 包结构设计

src/main/java/com/example/webapi/ ├── config/ # 配置类 ├── controller/ # 控制器 ├── service/ # 业务逻辑 ├── repository/ # 数据访问 ├── model/ # 数据模型 │ ├── entity/ # 实体类 │ ├── dto/ # 数据传输对象 │ └── vo/ # 视图对象 ├── exception/ # 异常处理 └── util/ # 工具类 

第四部分:数据模型设计

4.1 实体类设计

// User.javapackagecom.example.webapi.model.entity;importjavax.persistence.*;importjavax.validation.constraints.*;importjava.time.LocalDateTime;importjava.util.List;@Entity@Table(name ="users")publicclassUser{@Id@GeneratedValue(strategy =GenerationType.IDENTITY)privateLong id;@NotBlank(message ="用户名不能为空")@Size(min =3, max =50, message ="用户名长度必须在3-50字符之间")@Column(unique =true, nullable =false)privateString username;@Email(message ="邮箱格式不正确")@Column(unique =true, nullable =false)privateString email;@NotBlank(message ="密码不能为空")@Size(min =6, message ="密码长度至少6位")privateString password;privateString phone;@Enumerated(EnumType.STRING)privateUserStatus status =UserStatus.ACTIVE;@Column(name ="created_at")privateLocalDateTime createdAt;@Column(name ="updated_at")privateLocalDateTime updatedAt;// 构造方法publicUser(){this.createdAt =LocalDateTime.now();this.updatedAt =LocalDateTime.now();}// Getter和Setter方法// ... 省略具体实现}enumUserStatus{ ACTIVE, INACTIVE, DELETED }

4.2 DTO设计

// UserDTO.javapackagecom.example.webapi.model.dto;importjavax.validation.constraints.*;importjava.time.LocalDateTime;publicclassUserDTO{privateLong id;@NotBlank(message ="用户名不能为空")privateString username;@Email(message ="邮箱格式不正确")privateString email;privateString phone;privateLocalDateTime createdAt;// 构造方法publicUserDTO(){}// Getter和Setter// ... 省略具体实现}// CreateUserRequest.javapackagecom.example.webapi.model.dto;importjavax.validation.constraints.*;publicclassCreateUserRequest{@NotBlank(message ="用户名不能为空")@Size(min =3, max =50)privateString username;@Email@NotBlankprivateString email;@NotBlank@Size(min =6)privateString password;privateString phone;// Getter和Setter// ... 省略具体实现}

第五部分:数据访问层实现

5.1 Repository接口

// UserRepository.javapackagecom.example.webapi.repository;importcom.example.webapi.model.entity.User;importcom.example.webapi.model.entity.UserStatus;importorg.springframework.data.jpa.repository.JpaRepository;importorg.springframework.data.jpa.repository.Query;importorg.springframework.data.repository.query.Param;importorg.springframework.stereotype.Repository;importjava.util.List;importjava.util.Optional;@RepositorypublicinterfaceUserRepositoryextendsJpaRepository<User,Long>{Optional<User>findByUsername(String username);Optional<User>findByEmail(String email);List<User>findByStatus(UserStatus status);booleanexistsByUsername(String username);booleanexistsByEmail(String email);@Query("SELECT u FROM User u WHERE u.email LIKE %:email%")List<User>findByEmailContaining(@Param("email")String email);@Query("SELECT u FROM User u WHERE u.createdAt >= :startDate AND u.createdAt < :endDate")List<User>findUsersByCreateTimeRange(@Param("startDate")LocalDateTime startDate,@Param("endDate")LocalDateTime endDate);}

5.2 自定义Repository实现

// UserRepositoryCustom.javapackagecom.example.webapi.repository;importcom.example.webapi.model.entity.User;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.Pageable;importjava.util.List;publicinterfaceUserRepositoryCustom{Page<User>findUsersWithPagination(String keyword,Pageable pageable);List<User>findActiveUsersWithRecentActivity();}// UserRepositoryCustomImpl.javapackagecom.example.webapi.repository;importcom.example.webapi.model.entity.User;importcom.example.webapi.model.entity.UserStatus;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.PageImpl;importorg.springframework.data.domain.Pageable;importorg.springframework.stereotype.Repository;importjavax.persistence.EntityManager;importjavax.persistence.PersistenceContext;importjavax.persistence.TypedQuery;importjava.time.LocalDateTime;importjava.util.List;@RepositorypublicclassUserRepositoryCustomImplimplementsUserRepositoryCustom{@PersistenceContextprivateEntityManager entityManager;@OverridepublicPage<User>findUsersWithPagination(String keyword,Pageable pageable){String countQueryStr ="SELECT COUNT(u) FROM User u WHERE "+"(u.username LIKE :keyword OR u.email LIKE :keyword) AND u.status = 'ACTIVE'";TypedQuery<Long> countQuery = entityManager.createQuery(countQueryStr,Long.class); countQuery.setParameter("keyword","%"+ keyword +"%");Long total = countQuery.getSingleResult();String queryStr ="SELECT u FROM User u WHERE "+"(u.username LIKE :keyword OR u.email LIKE :keyword) AND u.status = 'ACTIVE' "+"ORDER BY u.createdAt DESC";TypedQuery<User> query = entityManager.createQuery(queryStr,User.class); query.setParameter("keyword","%"+ keyword +"%"); query.setFirstResult((int) pageable.getOffset()); query.setMaxResults(pageable.getPageSize());List<User> users = query.getResultList();returnnewPageImpl<>(users, pageable, total);}@OverridepublicList<User>findActiveUsersWithRecentActivity(){String queryStr ="SELECT u FROM User u WHERE u.status = 'ACTIVE' "+"AND u.updatedAt >= :recentTime";return entityManager.createQuery(queryStr,User.class).setParameter("recentTime",LocalDateTime.now().minusDays(7)).getResultList();}}

第六部分:业务逻辑层实现

6.1 Service接口设计

// UserService.javapackagecom.example.webapi.service;importcom.example.webapi.model.dto.CreateUserRequest;importcom.example.webapi.model.dto.UpdateUserRequest;importcom.example.webapi.model.dto.UserDTO;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.Pageable;importjava.util.List;publicinterfaceUserService{UserDTOcreateUser(CreateUserRequest request);UserDTOgetUserById(Long id);UserDTOgetUserByUsername(String username);Page<UserDTO>getAllUsers(Pageable pageable);List<UserDTO>searchUsers(String keyword);UserDTOupdateUser(Long id,UpdateUserRequest request);voiddeleteUser(Long id);booleanexistsByUsername(String username);booleanexistsByEmail(String email);}

6.2 Service实现类

// UserServiceImpl.javapackagecom.example.webapi.service.impl;importcom.example.webapi.model.dto.CreateUserRequest;importcom.example.webapi.model.dto.UpdateUserRequest;importcom.example.webapi.model.dto.UserDTO;importcom.example.webapi.model.entity.User;importcom.example.webapi.model.entity.UserStatus;importcom.example.webapi.repository.UserRepository;importcom.example.webapi.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.Pageable;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.stereotype.Service;importorg.springframework.transaction.annotation.Transactional;importjava.util.List;importjava.util.stream.Collectors;@Service@TransactionalpublicclassUserServiceImplimplementsUserService{@AutowiredprivateUserRepository userRepository;@AutowiredprivatePasswordEncoder passwordEncoder;@OverridepublicUserDTOcreateUser(CreateUserRequest request){// 检查用户名和邮箱是否已存在if(userRepository.existsByUsername(request.getUsername())){thrownewRuntimeException("用户名已存在");}if(userRepository.existsByEmail(request.getEmail())){thrownewRuntimeException("邮箱已存在");}// 创建用户实体User user =newUser(); user.setUsername(request.getUsername()); user.setEmail(request.getEmail()); user.setPassword(passwordEncoder.encode(request.getPassword())); user.setPhone(request.getPhone()); user.setStatus(UserStatus.ACTIVE);User savedUser = userRepository.save(user);returnconvertToDTO(savedUser);}@Override@Transactional(readOnly =true)publicUserDTOgetUserById(Long id){User user = userRepository.findById(id).orElseThrow(()->newRuntimeException("用户不存在"));returnconvertToDTO(user);}@Override@Transactional(readOnly =true)publicUserDTOgetUserByUsername(String username){User user = userRepository.findByUsername(username).orElseThrow(()->newRuntimeException("用户不存在"));returnconvertToDTO(user);}@Override@Transactional(readOnly =true)publicPage<UserDTO>getAllUsers(Pageable pageable){return userRepository.findAll(pageable).map(this::convertToDTO);}@Override@Transactional(readOnly =true)publicList<UserDTO>searchUsers(String keyword){return userRepository.findByEmailContaining(keyword).stream().map(this::convertToDTO).collect(Collectors.toList());}@OverridepublicUserDTOupdateUser(Long id,UpdateUserRequest request){User user = userRepository.findById(id).orElseThrow(()->newRuntimeException("用户不存在"));// 更新用户信息if(request.getEmail()!=null&&!request.getEmail().equals(user.getEmail())){if(userRepository.existsByEmail(request.getEmail())){thrownewRuntimeException("邮箱已存在");} user.setEmail(request.getEmail());}if(request.getPhone()!=null){ user.setPhone(request.getPhone());}User updatedUser = userRepository.save(user);returnconvertToDTO(updatedUser);}@OverridepublicvoiddeleteUser(Long id){User user = userRepository.findById(id).orElseThrow(()->newRuntimeException("用户不存在")); user.setStatus(UserStatus.DELETED); userRepository.save(user);}@Override@Transactional(readOnly =true)publicbooleanexistsByUsername(String username){return userRepository.existsByUsername(username);}@Override@Transactional(readOnly =true)publicbooleanexistsByEmail(String email){return userRepository.existsByEmail(email);}// 转换实体为DTOprivateUserDTOconvertToDTO(User user){UserDTO dto =newUserDTO(); dto.setId(user.getId()); dto.setUsername(user.getUsername()); dto.setEmail(user.getEmail()); dto.setPhone(user.getPhone()); dto.setCreatedAt(user.getCreatedAt());return dto;}}

第七部分:控制器层实现

7.1 基础控制器

// UserController.javapackagecom.example.webapi.controller;importcom.example.webapi.model.dto.CreateUserRequest;importcom.example.webapi.model.dto.UpdateUserRequest;importcom.example.webapi.model.dto.UserDTO;importcom.example.webapi.service.UserService;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.data.domain.Page;importorg.springframework.data.domain.PageRequest;importorg.springframework.data.domain.Pageable;importorg.springframework.data.domain.Sort;importorg.springframework.http.HttpStatus;importorg.springframework.http.ResponseEntity;importorg.springframework.validation.annotation.Validated;importorg.springframework.web.bind.annotation.*;importjavax.validation.Valid;importjava.util.HashMap;importjava.util.List;importjava.util.Map;@RestController@RequestMapping("/users")@ValidatedpublicclassUserController{@AutowiredprivateUserService userService;@PostMappingpublicResponseEntity<?>createUser(@Valid@RequestBodyCreateUserRequest request){try{UserDTO user = userService.createUser(request);returnResponseEntity.status(HttpStatus.CREATED).body(createSuccessResponse("用户创建成功", user));}catch(RuntimeException e){returnResponseEntity.badRequest().body(createErrorResponse(e.getMessage()));}}@GetMapping("/{id}")publicResponseEntity<?>getUserById(@PathVariableLong id){try{UserDTO user = userService.getUserById(id);returnResponseEntity.ok(createSuccessResponse("获取用户成功", user));}catch(RuntimeException e){returnResponseEntity.status(HttpStatus.NOT_FOUND).body(createErrorResponse(e.getMessage()));}}@GetMappingpublicResponseEntity<?>getAllUsers(@RequestParam(defaultValue ="0")int page,@RequestParam(defaultValue ="10")int size,@RequestParam(defaultValue ="createdAt")String sort){Pageable pageable =PageRequest.of(page, size,Sort.by(sort).descending());Page<UserDTO> users = userService.getAllUsers(pageable);Map<String,Object> response =newHashMap<>(); response.put("success",true); response.put("message","获取用户列表成功"); response.put("data", users.getContent()); response.put("currentPage", users.getNumber()); response.put("totalItems", users.getTotalElements()); response.put("totalPages", users.getTotalPages());returnResponseEntity.ok(response);}@GetMapping("/search")publicResponseEntity<?>searchUsers(@RequestParamString keyword){List<UserDTO> users = userService.searchUsers(keyword);returnResponseEntity.ok(createSuccessResponse("搜索用户成功", users));}@PutMapping("/{id}")publicResponseEntity<?>updateUser(@PathVariableLong id,@Valid@RequestBodyUpdateUserRequest request){try{UserDTO user = userService.updateUser(id, request);returnResponseEntity.ok(createSuccessResponse("用户更新成功", user));}catch(RuntimeException e){returnResponseEntity.badRequest().body(createErrorResponse(e.getMessage()));}}@DeleteMapping("/{id}")publicResponseEntity<?>deleteUser(@PathVariableLong id){try{ userService.deleteUser(id);returnResponseEntity.ok(createSuccessResponse("用户删除成功",null));}catch(RuntimeException e){returnResponseEntity.badRequest().body(createErrorResponse(e.getMessage()));}}// 工具方法:创建成功响应privateMap<String,Object>createSuccessResponse(String message,Object data){Map<String,Object> response =newHashMap<>(); response.put("success",true); response.put("message", message); response.put("data", data); response.put("timestamp",System.currentTimeMillis());return response;}// 工具方法:创建错误响应privateMap<String,Object>createErrorResponse(String message){Map<String,Object> response =newHashMap<>(); response.put("success",false); response.put("message", message); response.put("timestamp",System.currentTimeMillis());return response;}}

7.2 全局异常处理

// GlobalExceptionHandler.javapackagecom.example.webapi.exception;importorg.springframework.http.HttpStatus;importorg.springframework.http.ResponseEntity;importorg.springframework.validation.FieldError;importorg.springframework.web.bind.MethodArgumentNotValidException;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.RestControllerAdvice;importjavax.servlet.http.HttpServletRequest;importjava.util.HashMap;importjava.util.Map;@RestControllerAdvicepublicclassGlobalExceptionHandler{@ExceptionHandler(MethodArgumentNotValidException.class)publicResponseEntity<?>handleValidationExceptions(MethodArgumentNotValidException ex,HttpServletRequest request){Map<String,String> errors =newHashMap<>(); ex.getBindingResult().getAllErrors().forEach((error)->{String fieldName =((FieldError) error).getField();String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage);});Map<String,Object> response =newHashMap<>(); response.put("success",false); response.put("message","参数验证失败"); response.put("errors", errors); response.put("path", request.getRequestURI()); response.put("timestamp",System.currentTimeMillis());returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}@ExceptionHandler(RuntimeException.class)publicResponseEntity<?>handleRuntimeException(RuntimeException ex,HttpServletRequest request){Map<String,Object> response =newHashMap<>(); response.put("success",false); response.put("message", ex.getMessage()); response.put("path", request.getRequestURI()); response.put("timestamp",System.currentTimeMillis());returnResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);}@ExceptionHandler(Exception.class)publicResponseEntity<?>handleGlobalException(Exception ex,HttpServletRequest request){Map<String,Object> response =newHashMap<>(); response.put("success",false); response.put("message","服务器内部错误"); response.put("path", request.getRequestURI()); response.put("timestamp",System.currentTimeMillis());returnResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);}}

第八部分:安全配置

8.1 Spring Security配置

// SecurityConfig.javapackagecom.example.webapi.config;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.config.annotation.web.configuration.EnableWebSecurity;importorg.springframework.security.config.http.SessionCreationPolicy;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.web.SecurityFilterChain;@Configuration@EnableWebSecuritypublicclassSecurityConfig{@BeanpublicSecurityFilterChainfilterChain(HttpSecurity http)throwsException{ http .cors().and().csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests().antMatchers("/api/auth/**").permitAll().antMatchers("/api/users/create").permitAll().antMatchers("/api/public/**").permitAll().anyRequest().authenticated();return http.build();}@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}}

8.2 JWT认证配置

// JwtUtils.javapackagecom.example.webapi.util;importio.jsonwebtoken.*;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.stereotype.Component;importjava.util.Date;@ComponentpublicclassJwtUtils{@Value("${app.jwt.secret}")privateString jwtSecret;@Value("${app.jwt.expiration}")privateint jwtExpirationMs;publicStringgenerateJwtToken(String username){returnJwts.builder().setSubject(username).setIssuedAt(newDate()).setExpiration(newDate((newDate()).getTime()+ jwtExpirationMs)).signWith(SignatureAlgorithm.HS512, jwtSecret).compact();}publicStringgetUserNameFromJwtToken(String token){returnJwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();}publicbooleanvalidateJwtToken(String authToken){try{Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);returntrue;}catch(SignatureException e){// 日志记录}catch(MalformedJwtException e){// 日志记录}catch(ExpiredJwtException e){// 日志记录}catch(UnsupportedJwtException e){// 日志记录}catch(IllegalArgumentException e){// 日志记录}returnfalse;}}

第九部分:高级特性实现

9.1 缓存配置

// CacheConfig.javapackagecom.example.webapi.config;importorg.springframework.cache.CacheManager;importorg.springframework.cache.annotation.EnableCaching;importorg.springframework.cache.concurrent.ConcurrentMapCacheManager;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importjava.util.Arrays;@Configuration@EnableCachingpublicclassCacheConfig{@BeanpublicCacheManagercacheManager(){ConcurrentMapCacheManager cacheManager =newConcurrentMapCacheManager(); cacheManager.setCacheNames(Arrays.asList("users","products"));return cacheManager;}}// 在Service中使用缓存@ServicepublicclassUserServiceImplimplementsUserService{@Cacheable(value ="users", key ="#id")@OverridepublicUserDTOgetUserById(Long id){// 从数据库获取用户}@CacheEvict(value ="users", key ="#id")@OverridepublicUserDTOupdateUser(Long id,UpdateUserRequest request){// 更新用户}}

9.2 异步处理

// AsyncConfig.javapackagecom.example.webapi.config;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.annotation.EnableAsync;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importjava.util.concurrent.Executor;@Configuration@EnableAsyncpublicclassAsyncConfig{@Bean(name ="taskExecutor")publicExecutortaskExecutor(){ThreadPoolTaskExecutor executor =newThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix("AsyncThread-"); executor.initialize();return executor;}}// 异步服务@ServicepublicclassEmailService{@Async("taskExecutor")publicvoidsendWelcomeEmail(String email,String username){// 发送邮件的逻辑try{Thread.sleep(5000);// 模拟耗时操作System.out.println("欢迎邮件已发送至: "+ email);}catch(InterruptedException e){Thread.currentThread().interrupt();}}}

第十部分:测试

10.1 单元测试

// UserServiceTest.javapackagecom.example.webapi.service;importcom.example.webapi.model.dto.CreateUserRequest;importcom.example.webapi.model.dto.UserDTO;importcom.example.webapi.model.entity.User;importcom.example.webapi.repository.UserRepository;importorg.junit.jupiter.api.BeforeEach;importorg.junit.jupiter.api.Test;importorg.junit.jupiter.api.extension.ExtendWith;importorg.mockito.InjectMocks;importorg.mockito.Mock;importorg.mockito.junit.jupiter.MockitoExtension;importorg.springframework.security.crypto.password.PasswordEncoder;importjava.util.Optional;importstaticorg.junit.jupiter.api.Assertions.*;importstaticorg.mockito.ArgumentMatchers.any;importstaticorg.mockito.Mockito.*;@ExtendWith(MockitoExtension.class)classUserServiceTest{@MockprivateUserRepository userRepository;@MockprivatePasswordEncoder passwordEncoder;@InjectMocksprivateUserServiceImpl userService;privateCreateUserRequest createUserRequest;@BeforeEachvoidsetUp(){ createUserRequest =newCreateUserRequest(); createUserRequest.setUsername("testuser"); createUserRequest.setEmail("[email protected]"); createUserRequest.setPassword("password123"); createUserRequest.setPhone("13800138000");}@TestvoidcreateUser_Success(){// 准备when(userRepository.existsByUsername("testuser")).thenReturn(false);when(userRepository.existsByEmail("[email protected]")).thenReturn(false);when(passwordEncoder.encode("password123")).thenReturn("encodedPassword");User savedUser =newUser(); savedUser.setId(1L); savedUser.setUsername("testuser"); savedUser.setEmail("[email protected]");when(userRepository.save(any(User.class))).thenReturn(savedUser);// 执行UserDTO result = userService.createUser(createUserRequest);// 验证assertNotNull(result);assertEquals(1L, result.getId());assertEquals("testuser", result.getUsername());assertEquals("[email protected]", result.getEmail());verify(userRepository,times(1)).save(any(User.class));}@TestvoidgetUserById_UserExists(){// 准备User user =newUser(); user.setId(1L); user.setUsername("testuser"); user.setEmail("[email protected]");when(userRepository.findById(1L)).thenReturn(Optional.of(user));// 执行UserDTO result = userService.getUserById(1L);// 验证assertNotNull(result);assertEquals(1L, result.getId());assertEquals("testuser", result.getUsername());}}

10.2 集成测试

// UserControllerIntegrationTest.javapackagecom.example.webapi.controller;importcom.example.webapi.model.dto.CreateUserRequest;importcom.example.webapi.repository.UserRepository;importcom.fasterxml.jackson.databind.ObjectMapper;importorg.junit.jupiter.api.Test;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;importorg.springframework.boot.test.context.SpringBootTest;importorg.springframework.http.MediaType;importorg.springframework.test.web.servlet.MockMvc;importorg.springframework.transaction.annotation.Transactional;importstaticorg.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;importstaticorg.springframework.test.web.servlet.result.MockMvcResultMatchers.*;@SpringBootTest@AutoConfigureMockMvc@TransactionalclassUserControllerIntegrationTest{@AutowiredprivateMockMvc mockMvc;@AutowiredprivateObjectMapper objectMapper;@AutowiredprivateUserRepository userRepository;@TestvoidcreateUser_ValidRequest_ReturnsCreated()throwsException{CreateUserRequest request =newCreateUserRequest(); request.setUsername("integrationtest"); request.setEmail("[email protected]"); request.setPassword("password123"); mockMvc.perform(post("/api/users").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request))).andExpect(status().isCreated()).andExpect(jsonPath("$.success").value(true)).andExpect(jsonPath("$.data.username").value("integrationtest"));}@TestvoidgetUserById_UserExists_ReturnsUser()throwsException{// 先创建用户CreateUserRequest request =newCreateUserRequest(); request.setUsername("testuser"); request.setEmail("[email protected]"); request.setPassword("password123");String response = mockMvc.perform(post("/api/users").contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(request))).andReturn().getResponse().getContentAsString();// 提取用户ID并查询// 这里简化处理,实际应该解析响应获取ID mockMvc.perform(get("/api/users/1")).andExpect(status().isOk()).andExpect(jsonPath("$.success").value(true));}}

第十一部分:部署与监控

11.1 Docker配置

# Dockerfile FROM openjdk:11-jre-slim WORKDIR /app COPY target/webapi-demo-1.0.0.jar app.jar RUN sh -c 'touch /app.jar' ENV EXPOSE 8080 ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar" ] 
# docker-compose.ymlversion:'3.8'services:webapi:build: . ports:-"8080:8080"environment:- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/webapi_db - SPRING_DATASOURCE_USERNAME=root - SPRING_DATASOURCE_PASSWORD=password depends_on:- mysql mysql:image: mysql:8.0environment:- MYSQL_ROOT_PASSWORD=password - MYSQL_DATABASE=webapi_db ports:-"3306:3306"volumes:- mysql_data:/var/lib/mysql volumes:mysql_data:

11.2 健康检查与监控

// HealthCheckController.javapackagecom.example.webapi.controller;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.boot.actuate.health.HealthComponent;importorg.springframework.boot.actuate.health.HealthEndpoint;importorg.springframework.jdbc.core.JdbcTemplate;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RestController;importjava.util.HashMap;importjava.util.Map;@RestController@RequestMapping("/health")publicclassHealthCheckController{@AutowiredprivateJdbcTemplate jdbcTemplate;@AutowiredprivateHealthEndpoint healthEndpoint;@GetMappingpublicMap<String,Object>healthCheck(){Map<String,Object> health =newHashMap<>();// 数据库健康检查try{ jdbcTemplate.execute("SELECT 1"); health.put("database","UP");}catch(Exception e){ health.put("database","DOWN");}// 系统健康检查HealthComponent systemHealth = healthEndpoint.health(); health.put("status", systemHealth.getStatus().getCode()); health.put("timestamp",System.currentTimeMillis());return health;}}

第十二部分:最佳实践与总结

12.1 API设计最佳实践

  1. 使用合适的HTTP状态码
    • 200: 成功
    • 201: 创建成功
    • 400: 客户端错误
    • 401: 未授权
    • 403: 禁止访问
    • 404: 资源不存在
    • 500: 服务器错误
  2. 统一的响应格式
{"success":true,"message":"操作成功","data":{},"timestamp":1640995200000}
  1. 版本控制
    • URL路径版本: /api/v1/users
    • 请求头版本: Accept: application/vnd.example.v1+json
  2. 分页和过滤
    • GET /api/users?page=0&size=10&sort=createdAt,desc
    • GET /api/users?name=john&email=example.com

12.2 性能优化建议

  1. 数据库优化
    • 合理使用索引
    • 避免N+1查询问题
    • 使用连接查询替代多次查询
  2. 缓存策略
    • 使用Redis进行会话存储
    • 缓存热点数据
    • 设置合理的缓存过期时间
  3. 异步处理
    • 使用消息队列处理耗时操作
    • 异步发送邮件和通知
    • 后台任务处理

12.3 安全考虑

  1. 输入验证
    • 使用Bean Validation注解
    • 防范SQL注入
    • XSS防护
  2. 认证授权
    • 使用JWT进行无状态认证
    • 基于角色的访问控制
    • API密钥管理
  3. 其他安全措施
    • HTTPS强制使用
    • 定期更新依赖
    • 安全头部配置

12.4 总结

通过本文的详细讲解,您应该已经掌握了Java后端Web API开发的全流程。从环境搭建、项目架构设计,到具体的编码实现和测试部署,我们覆盖了开发一个完整Web API项目所需的所有关键知识点。

核心要点回顾:

  • 采用分层架构,保持代码清晰和可维护性
  • 使用Spring Boot快速开发,减少配置工作
  • 实现完整的CRUD操作和业务逻辑
  • 添加适当的异常处理和日志记录
  • 编写全面的测试用例
  • 考虑安全性和性能优化

在实际项目开发中,还需要根据具体需求不断调整和优化架构设计,同时关注代码质量、团队协作和持续集成等工程实践。希望本文能为您的Java Web API开发之旅提供有力的帮助!

Read more

OpenClaw(Clawdbot)插件更新,新增支持在面板一键QQ和飞书机器人

OpenClaw(Clawdbot)插件更新,新增支持在面板一键QQ和飞书机器人

这次,OpenClaw 插件迎来了一次重要更新。 现在,你可以直接在插件中配置 飞书机器人或 QQ 机器人,让 OpenClaw 真正走出 Web 界面,进入你日常使用的消息工具中。 无需额外部署服务,配置完成后即可开始对话。 重要提示:由于官方更改包名,不支持直接升级,如需更新请卸载旧版插件,安装新版OpenClaw插件,已有数据会丢失,请您评估是否需要更新,新安装不受影响。 配置QQ机器人1. 打开QQ开放平台,注册账号,如已注册可直接登陆 点击编辑 IP 白名单,填写服务器 IP 并保存 点击开发管理,获取APPID、AppSecret 创建完成后点击刚刚创建的机器人 填写机器人基础信息 登录后点击机器人,创建机器人 按提示完成登录 8.将获取到的信息填写到插件,并保存启用 添加后即可在群聊中进行对话 在此处添加完成后回到QQ-群管理-添加机器人,在其他页面找到机器人 选择需要使用的群聊 回到QQ机器人平台,

By Ne0inhk
【火】Spatial Joy 2025 全球 AR&AI 赛事:开发者要的资源、玩法、避坑攻略都在这

【火】Spatial Joy 2025 全球 AR&AI 赛事:开发者要的资源、玩法、避坑攻略都在这

Spatial Joy 2025 Rokid乐奇 全球 AR&AI 开发大赛 值不值得参加?不少参加过连续两届 Rokid乐奇 赛事的老兵,纷纷表示非常值得参加。 先说最实在的——奖金。 AR赛道分为应用和游戏两个赛道,金奖各20万人民币,而且是现金!交完税全是你自己的!这还不够,AR赛道总共设了27个奖项,据我打听到的往年数据,能正常跑进初赛的作品大概就60-70个,这意味着获奖比例相当高。 20万就封顶了吗?远远没有!亚马孙科技给使用Kiro并获奖的开发者,在原奖金基础上再加20%现金奖励! AI赛道同样设置了27个奖项,奖金从1万到5万不等,主要以智能体开发为主,支持市面上所有智能体平台的适配。也就是说,你之前做的智能体微调一下就能参赛! 更重要的是,现在正是智能眼镜行业爆发前夜。据我观察,未来2-3年将是空间计算应用落地的关键窗口期,提前布局的开发者将占据绝对先发优势。 好了,重磅消息说完,下面是我为大家整理的详细参赛指南: 先给开发者交个底:这赛事值得花时间吗? 对技术人来说,一场赛事值不值得冲,就看三点:资源给不给力、

By Ne0inhk

2026年RAG技术路线图:基于DeepSeek与Neo4j知识图谱构建企业智能体系

RAG的演进:为何图检索增强生成(GraphRAG)将主导2026年 检索增强生成(RAG)自问世以来经历了深刻变革,2026年标志着其向图检索增强生成(GraphRAG)范式的关键性转变。这一演进源于传统平面向量型RAG在满足企业级复杂推理和可靠决策支持需求方面日益凸显的局限性。 这一转型的核心驱动力是从平面向量相似性向复杂关系推理的跨越。传统RAG依赖向量嵌入来衡量查询与文档片段的语义相似性,但这种方法无法捕捉企业决策至关重要的实体、概念与事件间的复杂关联。相比之下,GraphRAG将信息构建为包含节点(实体)和边(关系)的知识图谱,使模型能够遍历并推理这些关联——解锁了平面向量RAG无法实现的多跳推理和上下文关系理解能力。 GraphRAG还解决了传统RAG的两大长期痛点:上下文窗口限制和“中间信息丢失”问题。随着企业查询日益复杂,需要更大的上下文窗口来整合相关信息,但即便是最先进的大语言模型(LLM)也存在有限的上下文容量。GraphRAG通过将结构化知识存储在外部图数据库中解决了这一问题,允许模型按需检索最相关的节点和关系,而非将大量文本塞入上下文窗口。此外,“中间信息

By Ne0inhk
Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线

Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 bip340 适配鸿蒙 HarmonyOS 实战:次世代 Schnorr 签名,为鸿蒙 Web3 与隐私计算筑牢加密防线 前言 在鸿蒙(OpenHarmony)生态迈向去中心化金融(DeFi)、隐私通讯及安全资产管理等高阶安全场景的背景下,如何实现更高性能、更具扩展性且抗攻击能力的数字签名架构,已成为决定应用闭环安全性的“压舱石”。在鸿蒙设备这类强调分布式鉴权与芯片级安全(TEE/SE)的移动终端上,如果依然沿用传统的 ECDSA 签名算法,由于由于其固有的可延展性风险与高昂的聚合验证成本,极易由于由于在大规模节点验证时的 CPU 负载过高导致交互滞后。 我们需要一种能够实现签名线性聚合、计算逻辑极简且具备原生抗延展性的密码学方案。 bip340 为 Flutter 开发者引入了比特币 Taproot 升级的核心——Schnorr 签名算法。它不仅在安全性上超越了传统标准,更通过其线性的数学特性,

By Ne0inhk