Web 开发
LCGYL Framework 提供完整的 Web 开发支持,包括 RESTful API、请求处理、响应渲染等功能。
快速开始
创建控制器
java
@Controller
@RequestMapping("/api/users")
public class UserController {
@Inject
private UserService userService;
@GetMapping
public List<User> list() {
return userService.findAll();
}
@GetMapping("/{id}")
public User get(@PathVariable Long id) {
return userService.findById(id);
}
@PostMapping
public User create(@RequestBody User user) {
return userService.create(user);
}
@PutMapping("/{id}")
public User update(@PathVariable Long id, @RequestBody User user) {
return userService.update(id, user);
}
@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
userService.delete(id);
}
}请求映射
HTTP 方法
java
@Controller
@RequestMapping("/api/orders")
public class OrderController {
@GetMapping // GET 请求
public List<Order> list() { }
@PostMapping // POST 请求
public Order create() { }
@PutMapping("/{id}") // PUT 请求
public Order update() { }
@PatchMapping("/{id}") // PATCH 请求
public Order patch() { }
@DeleteMapping("/{id}") // DELETE 请求
public void delete() { }
}路径变量
java
@GetMapping("/users/{userId}/orders/{orderId}")
public Order getOrder(
@PathVariable Long userId,
@PathVariable Long orderId) {
return orderService.findByUserIdAndOrderId(userId, orderId);
}
// 正则表达式约束
@GetMapping("/files/{filename:.+}")
public Resource getFile(@PathVariable String filename) {
return fileService.getFile(filename);
}请求参数
java
@GetMapping("/search")
public List<User> search(
@RequestParam String keyword,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
return userService.search(keyword, status, page, size);
}请求头
java
@GetMapping("/info")
public UserInfo getInfo(
@RequestHeader("Authorization") String token,
@RequestHeader(value = "X-Request-Id", required = false) String requestId) {
return userService.getInfo(token);
}请求体
JSON 请求体
java
@PostMapping("/users")
public User createUser(@RequestBody CreateUserRequest request) {
return userService.create(request);
}
public record CreateUserRequest(
String name,
String email,
String password
) {}表单数据
java
@PostMapping("/login")
public AuthResult login(
@RequestParam String username,
@RequestParam String password) {
return authService.login(username, password);
}文件上传
java
@PostMapping("/upload")
public FileInfo upload(@RequestParam("file") MultipartFile file) {
String filename = file.getOriginalFilename();
byte[] content = file.getBytes();
return fileService.save(filename, content);
}
@PostMapping("/upload/multiple")
public List<FileInfo> uploadMultiple(@RequestParam("files") List<MultipartFile> files) {
return files.stream()
.map(file -> fileService.save(file.getOriginalFilename(), file.getBytes()))
.collect(Collectors.toList());
}响应处理
返回 JSON
java
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.findById(id); // 自动转换为 JSON
}自定义响应
java
@GetMapping("/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(user);
}
@PostMapping("/users")
public ResponseEntity<User> createUser(@RequestBody User user) {
User created = userService.create(user);
URI location = URI.create("/api/users/" + created.getId());
return ResponseEntity.created(location).body(created);
}统一响应格式
java
public record ApiResponse<T>(
int code,
String message,
T data
) {
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(200, "success", data);
}
public static <T> ApiResponse<T> error(int code, String message) {
return new ApiResponse<>(code, message, null);
}
}
@GetMapping("/users/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {
User user = userService.findById(id);
return ApiResponse.success(user);
}文件下载
java
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> download(@PathVariable String filename) {
Resource resource = fileService.getFile(filename);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + filename + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
}参数验证
使用验证注解
java
public record CreateUserRequest(
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度必须在2-20之间")
String name,
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
String email,
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度必须在6-20之间")
String password,
@Min(value = 0, message = "年龄不能为负数")
@Max(value = 150, message = "年龄不能超过150")
Integer age
) {}
@PostMapping("/users")
public User createUser(@Valid @RequestBody CreateUserRequest request) {
return userService.create(request);
}自定义验证器
java
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = PhoneValidator.class)
public @interface Phone {
String message() default "手机号格式不正确";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class PhoneValidator implements ConstraintValidator<Phone, String> {
private static final Pattern PHONE_PATTERN =
Pattern.compile("^1[3-9]\\d{9}$");
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
return PHONE_PATTERN.matcher(value).matches();
}
}
// 使用
public record CreateUserRequest(
@Phone
String phone
) {}异常处理
全局异常处理
java
@ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(NotFoundException.class)
public ResponseEntity<ApiResponse<Void>> handleNotFound(NotFoundException e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND)
.body(ApiResponse.error(404, e.getMessage()));
}
@ExceptionHandler(ValidationException.class)
public ResponseEntity<ApiResponse<Void>> handleValidation(ValidationException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(ApiResponse.error(400, e.getMessage()));
}
@ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ApiResponse<Void>> handleUnauthorized(UnauthorizedException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
.body(ApiResponse.error(401, e.getMessage()));
}
@ExceptionHandler(ForbiddenException.class)
public ResponseEntity<ApiResponse<Void>> handleForbidden(ForbiddenException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(ApiResponse.error(403, e.getMessage()));
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ApiResponse<Void>> handleException(Exception e) {
logger.error("未处理的异常", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ApiResponse.error(500, "服务器内部错误"));
}
}验证异常处理
java
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiResponse<Map<String, String>>> handleValidationErrors(
MethodArgumentNotValidException e) {
Map<String, String> errors = new HashMap<>();
e.getBindingResult().getFieldErrors().forEach(error ->
errors.put(error.getField(), error.getDefaultMessage())
);
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ApiResponse<>(400, "参数验证失败", errors));
}过滤器和拦截器
过滤器
java
@Component
@Order(1)
public class LoggingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
long startTime = System.currentTimeMillis();
logger.info("请求开始: {} {}", httpRequest.getMethod(), httpRequest.getRequestURI());
try {
chain.doFilter(request, response);
} finally {
long duration = System.currentTimeMillis() - startTime;
logger.info("请求结束: {} {} - {}ms",
httpRequest.getMethod(),
httpRequest.getRequestURI(),
duration);
}
}
}拦截器
java
@Component
public class AuthenticationInterceptor implements HandlerInterceptor {
@Inject
private TokenService tokenService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// 跳过不需要认证的路径
if (isPublicPath(request.getRequestURI())) {
return true;
}
String token = extractToken(request);
if (token == null || !tokenService.validateToken(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write("{\"code\":401,\"message\":\"未授权\"}");
return false;
}
User user = tokenService.getUserFromToken(token);
SecurityContext.setCurrentUser(user);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
SecurityContext.clear();
}
}注册拦截器
java
@Component
public class WebConfig implements WebMvcConfigurer {
@Inject
private AuthenticationInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authInterceptor)
.addPathPatterns("/api/**")
.excludePathPatterns("/api/auth/**", "/api/public/**");
}
}CORS 配置
全局 CORS
java
@Component
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedOrigins("http://localhost:3000", "https://example.com")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}控制器级别 CORS
java
@Controller
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:3000")
public class UserController {
// ...
}分页
分页请求
java
@GetMapping("/users")
public Page<User> listUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "asc") String sortDir) {
PageRequest pageRequest = new PageRequest(page, size, sortBy, sortDir);
return userService.findAll(pageRequest);
}分页响应
java
public record Page<T>(
List<T> content,
int page,
int size,
long totalElements,
int totalPages,
boolean first,
boolean last
) {}API 版本控制
URL 版本
java
@Controller
@RequestMapping("/api/v1/users")
public class UserControllerV1 {
// V1 版本
}
@Controller
@RequestMapping("/api/v2/users")
public class UserControllerV2 {
// V2 版本
}Header 版本
java
@Controller
@RequestMapping("/api/users")
public class UserController {
@GetMapping(headers = "X-API-Version=1")
public List<UserV1> listV1() {
// V1 版本
}
@GetMapping(headers = "X-API-Version=2")
public List<UserV2> listV2() {
// V2 版本
}
}WebSocket
配置 WebSocket
java
@Component
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatHandler(), "/ws/chat")
.setAllowedOrigins("*");
}
@Bean
public WebSocketHandler chatHandler() {
return new ChatWebSocketHandler();
}
}WebSocket 处理器
java
@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {
private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
@Override
public void afterConnectionEstablished(WebSocketSession session) {
sessions.add(session);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
// 广播消息
sessions.forEach(s -> {
try {
s.sendMessage(message);
} catch (IOException e) {
// 处理异常
}
});
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
sessions.remove(session);
}
}最佳实践
1. RESTful 设计
java
// ✅ 推荐:使用名词
GET /api/users // 获取用户列表
GET /api/users/{id} // 获取单个用户
POST /api/users // 创建用户
PUT /api/users/{id} // 更新用户
DELETE /api/users/{id} // 删除用户
// ❌ 不推荐:使用动词
GET /api/getUsers
POST /api/createUser
POST /api/deleteUser2. 统一响应格式
java
// ✅ 推荐:统一格式
{
"code": 200,
"message": "success",
"data": { ... }
}3. 合理使用 HTTP 状态码
java
// ✅ 推荐
200 OK // 成功
201 Created // 创建成功
204 No Content // 删除成功
400 Bad Request // 参数错误
401 Unauthorized // 未认证
403 Forbidden // 无权限
404 Not Found // 资源不存在
500 Internal Server Error // 服务器错误4. 参数验证
java
// ✅ 推荐:使用验证注解
@PostMapping("/users")
public User create(@Valid @RequestBody CreateUserRequest request) { }常见问题
Q: 如何处理跨域问题?
A: 配置 CORS 或使用代理。
Q: 如何处理大文件上传?
A:
- 配置文件大小限制
- 使用分片上传
- 异步处理
Q: 如何实现接口限流?
A: 使用限流注解或过滤器。
java
@RateLimit(limit = 100, period = 60)
@GetMapping("/api/data")
public Data getData() { }