快学快用系列:一文学会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卸载服务,299元可上门“帮卸”;2026年春招AI人才身价暴涨:平均月薪超6万;Meta辟谣亚历山大·王离职 | 极客头条

二手平台出现OpenClaw卸载服务,299元可上门“帮卸”;2026年春招AI人才身价暴涨:平均月薪超6万;Meta辟谣亚历山大·王离职 | 极客头条

「极客头条」—— 技术人员的新闻圈! ZEEKLOG 的读者朋友们好,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧。(投稿或寻求报道:[email protected]) 整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 一分钟速览新闻点! * 微信员工辟谣“小龙虾可自动发红包”:不要以讹传讹 * 蚂蚁集团启动春招,超 70% 为 AI 相关岗位 * 受贿 208 万!拼多多一员工被抓 * 2026 年春招 AI 人才身价暴涨: 平均月薪超 6 万元 * 二手平台出现 OpenClaw 上门卸载服务 * 权限太高,国家互联网应急中心发布 OpenClaw 安全应用的风险提示 * 字节豆包内测 AI 电商功能:无需跳转抖音,日活用户数超

By Ne0inhk
遭“美国政府封杀”后,Anthropic正式提起诉讼!

遭“美国政府封杀”后,Anthropic正式提起诉讼!

整理 | 苏宓 出品 | ZEEKLOG(ID:ZEEKLOGnews) 据路透社报道,当地时间周一,AI 初创公司 Anthropic 正式对美国国防部及特朗普政府提起诉讼,抗议五角大楼将其列为“国家安全供应链风险”主体的决定。 Anthropic 在向美国加州北区地方法院提交的诉讼文件中表示,这一认定“史无前例且非法”,已对公司造成“不可挽回的损害”。公司希望法院撤销该决定,并指示联邦机构停止执行相关认定。 划定 AI 应用红线,双方观点不一 正如我们此前报道,这场争端的核心在于 Anthropic 为其核心 AI 模型 Claude 设定的两条技术使用红线,与美国国防部的使用需求发生根本冲突。 此前,Anthropic 曾与五角大楼签署一份价值最高可达 2 亿美元的合作合同,Claude 也成为少数被纳入美国机密网络环境进行测试的 AI 系统之一。 对此,Anthropic 一直坚持两条底线: * Claude 等技术不得被用于对美国民众的大规模国内监控;

By Ne0inhk
为省5-10美元差点毁库!Claude一条指令删光200万条数据、网站停摆24小时,创始人坦言:全是我的错

为省5-10美元差点毁库!Claude一条指令删光200万条数据、网站停摆24小时,创始人坦言:全是我的错

编译 | 屠敏 出品 | ZEEKLOG(ID:ZEEKLOGnews) AI 时代,一次看似普通的操作,竟能让整套生产环境与近 200 万条数据瞬间「归零」。 近日,数据科学社区 DataTalks.Club 创始人 Alexey Grigorev 就遭遇了这样的惊魂时刻,他在使用 AI 编程工具 Claude Code 管理网站服务器时,意外清空了平台积累 2.5 年的核心数据,甚至连数据库快照也未能幸免,导致网站停摆整整 24 小时。 这起事故不仅在开发者社区引发热议,更给所有依赖 AI 工具与自动化运维的从业者敲响了警钟。事后,Alexey Grigorev 公开复盘了整个过程,并揭露了此次事故的核心问题。让我们一起看看。 一次看似很普通的网站迁移 这场“删库”事件的前因,其实并不复杂。

By Ne0inhk
星标超 28 万,OpenClaw 两天两次大更!适配GPT 5.4,告别“抽卡式 Prompt”

星标超 28 万,OpenClaw 两天两次大更!适配GPT 5.4,告别“抽卡式 Prompt”

整理 | 梦依丹 出品 | ZEEKLOG(ID:ZEEKLOGnews) “We don’t do small releases.” 这是 OpenClaw 在发布 2026.3.7 版本时写下的一句话。 刚刚过去的周六与周日,这个 GitHub 星标已超 28 万 的 AI Agent 开源项目再次迎来两轮重量级更新。 两天两次更新:OpenClaw 做了一次“真正的大版本升级” 打开 OpenClaw 的 GitHub 更新日志,你会发现这次版本更新的规模确实不小。在 3 月 7 日发布更新后,第二天又迅速推出 2026.3.8-beta.1 和

By Ne0inhk