Skip to content

Commit 72369be

Browse files
authored
(#25) 경로 변수 및 요청 파라미터 유효성 검사
* feat: 입력 검증 추가 - 경로 변수 및 요청 파라미터 유효성 검사 - UserController: username 경로 변수에 GitHub 사용자명 패턴 검증 추가 - RankingController: page 파라미터에 최소값 검증 추가 - GlobalExceptionHandler: ConstraintViolationException 처리 추가 * refactor: GitHub username 연속 하이픈 금지
1 parent 1096b8f commit 72369be

3 files changed

Lines changed: 29 additions & 3 deletions

File tree

src/main/java/com/gitranker/api/domain/ranking/RankingController.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@
33
import com.gitranker.api.domain.ranking.dto.RankingList;
44
import com.gitranker.api.domain.user.Tier;
55
import com.gitranker.api.global.response.ApiResponse;
6+
import jakarta.validation.constraints.Min;
67
import lombok.RequiredArgsConstructor;
8+
import org.springframework.validation.annotation.Validated;
79
import org.springframework.web.bind.annotation.GetMapping;
810
import org.springframework.web.bind.annotation.RequestMapping;
911
import org.springframework.web.bind.annotation.RequestParam;
1012
import org.springframework.web.bind.annotation.RestController;
1113

14+
@Validated
1215
@RequiredArgsConstructor
1316
@RestController
1417
@RequestMapping("/api/v1/ranking")
@@ -18,7 +21,7 @@ public class RankingController {
1821

1922
@GetMapping
2023
public ApiResponse<RankingList> getRankings(
21-
@RequestParam(defaultValue = "0") int page,
24+
@RequestParam(defaultValue = "0") @Min(value = 0, message = "페이지 번호는 0 이상이어야 합니다") int page,
2225
@RequestParam(required = false) Tier tier
2326
) {
2427
RankingList response = rankingService.getRankingList(page, tier);

src/main/java/com/gitranker/api/domain/user/UserController.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,37 @@
88
import com.gitranker.api.global.error.exception.BusinessException;
99
import com.gitranker.api.global.response.ApiResponse;
1010
import jakarta.servlet.http.HttpServletResponse;
11+
import jakarta.validation.constraints.Pattern;
1112
import lombok.RequiredArgsConstructor;
1213
import org.springframework.http.ResponseEntity;
1314
import org.springframework.security.core.annotation.AuthenticationPrincipal;
15+
import org.springframework.validation.annotation.Validated;
1416
import org.springframework.web.bind.annotation.*;
1517

18+
@Validated
1619
@RequiredArgsConstructor
1720
@RestController
1821
@RequestMapping("/api/v1/users")
1922
public class UserController {
23+
24+
private static final String USERNAME_PATTERN = "^(?=.{1,39}$)[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*$";
25+
private static final String USERNAME_MESSAGE = "GitHub 사용자명은 1-39자의 영문, 숫자, 하이픈만 허용됩니다";
2026
private final UserQueryService userQueryService;
2127
private final UserRefreshService userRefreshService;
2228
private final UserDeletionService userDeletionService;
2329

2430
@GetMapping("/{username}")
25-
public ApiResponse<RegisterUserResponse> getUser(@PathVariable String username) {
31+
public ApiResponse<RegisterUserResponse> getUser(
32+
@PathVariable @Pattern(regexp = USERNAME_PATTERN, message = USERNAME_MESSAGE) String username
33+
) {
2634
RegisterUserResponse response = userQueryService.findByUsername(username);
2735

2836
return ApiResponse.success(response);
2937
}
3038

3139
@PostMapping("/{username}/refresh")
3240
public ApiResponse<RegisterUserResponse> refreshUser(
33-
@PathVariable String username,
41+
@PathVariable @Pattern(regexp = USERNAME_PATTERN, message = USERNAME_MESSAGE) String username,
3442
@AuthenticationPrincipal User user
3543
) {
3644
if (user == null) {

src/main/java/com/gitranker/api/global/error/GlobalExceptionHandler.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.gitranker.api.global.response.ApiResponse;
66
import com.gitranker.api.global.util.TimeUtils;
77
import jakarta.servlet.http.HttpServletRequest;
8+
import jakarta.validation.ConstraintViolationException;
89
import lombok.RequiredArgsConstructor;
910
import lombok.extern.slf4j.Slf4j;
1011
import org.springframework.http.HttpStatus;
@@ -35,6 +36,20 @@ public ResponseEntity<ApiResponse<Object>> handleBusinessException(BusinessExcep
3536
.body(ApiResponse.error(errorType, e.getData()));
3637
}
3738

39+
@ExceptionHandler(ConstraintViolationException.class)
40+
public ResponseEntity<ApiResponse<Object>> handleConstraintViolationException(ConstraintViolationException e) {
41+
String message = e.getConstraintViolations().stream()
42+
.map(violation -> violation.getMessage())
43+
.findFirst()
44+
.orElse("잘못된 요청입니다");
45+
46+
log.warn("입력값 검증 실패 - Message: {}", message);
47+
48+
return ResponseEntity
49+
.status(HttpStatus.BAD_REQUEST)
50+
.body(ApiResponse.error(ErrorType.INVALID_REQUEST, message));
51+
}
52+
3853
@ExceptionHandler(NoResourceFoundException.class)
3954
public Object handleNoResourceFoundException(NoResourceFoundException e, HttpServletRequest request) {
4055
log.debug("리소스 없음 - Path: {}", e.getResourcePath());

0 commit comments

Comments
 (0)