← Назад к вопросам

Как достать id из пути в контроллере

1.0 Junior🔥 231 комментариев
#REST API и микросервисы#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Как достать ID из пути в Spring контроллере

Это один из основных навыков в Spring MVC/Boot разработке. ID в пути URL — это path variable, и Spring предоставляет несколько способов это сделать.

Основной способ: @PathVariable

1. Простой случай

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    // GET /api/users/123
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
        User user = userService.findById(id);
        return ResponseEntity.ok(new UserDTO(user));
    }
}

Как работает:

  • URL path: /api/users/123
  • Spring автоматически извлекает 123 из {id}
  • Преобразует в Long (или другой тип)
  • Передаёт в метод как параметр

2. Несколько ID в пути

@GetMapping("/{userId}/posts/{postId}")
public ResponseEntity<PostDTO> getUserPost(
    @PathVariable Long userId,
    @PathVariable Long postId
) {
    Post post = postService.findById(userId, postId);
    return ResponseEntity.ok(new PostDTO(post));
}

// GET /api/users/123/posts/456
// userId = 123
// postId = 456

Явное указание имени переменной

Когда имя в пути отличается от параметра

@GetMapping("/user/{uid}")
public ResponseEntity<UserDTO> getUser(
    @PathVariable("uid") Long id  // "uid" в пути, но параметр "id"
) {
    return ResponseEntity.ok(userService.findById(id));
}

// GET /api/user/123
// Spring ищет {uid} в пути и передаёт как параметр id

Разные типы данных

UUID (Universally Unique Identifier)

@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable UUID id) {
    User user = userService.findById(id);
    return ResponseEntity.ok(new UserDTO(user));
}

// GET /api/users/550e8400-e29b-41d4-a716-446655440000
// id = UUID.fromString("550e8400-e29b-41d4-a716-446655440000")

String

@GetMapping("/user/{username}")
public ResponseEntity<UserDTO> getUserByUsername(@PathVariable String username) {
    User user = userService.findByUsername(username);
    return ResponseEntity.ok(new UserDTO(user));
}

// GET /api/user/john_doe
// username = "john_doe"

Опциональный ID (required = false)

Когда ID может быть не обязательным

@GetMapping("/items/{id}")
public ResponseEntity<ItemDTO> getItem(
    @PathVariable(required = false) Long id
) {
    if (id == null) {
        return ResponseEntity.ok(itemService.getDefault());
    }
    return ResponseEntity.ok(itemService.findById(id));
}

Валидация ID

С аннотацией @Validated

@RestController
@Validated  // Включить валидацию
@RequestMapping("/api/users")
public class UserController {
    
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(
        @PathVariable 
        @Positive(message = "ID должен быть положительным числом")
        Long id
    ) {
        User user = userService.findById(id);
        return ResponseEntity.ok(new UserDTO(user));
    }
}

Валидация происходит:

  • Если id <= 0, возвращается 400 Bad Request
  • Message будет в ответе

Custom валидация

@GetMapping("/user/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
    if (id <= 0) {
        throw new IllegalArgumentException("ID должен быть положительным");
    }
    User user = userService.findById(id)
            .orElseThrow(() -> new UserNotFoundException("User with id " + id + " not found"));
    return ResponseEntity.ok(new UserDTO(user));
}

Regex в пути

Когда нужна валидация формата

@GetMapping("/users/{id:[0-9]+}")  // Только числа
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
    return ResponseEntity.ok(userService.findById(id));
}

// GET /api/users/123 → ✅ OK
// GET /api/users/abc → ❌ 404 Not Found (путь не соответствует regex)

UUID regex

@GetMapping("/users/{id:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}}")
public ResponseEntity<UserDTO> getUser(@PathVariable UUID id) {
    return ResponseEntity.ok(userService.findById(id));
}

Получение всех path variables сразу

Через Map

@GetMapping("/users/{userId}/posts/{postId}")
public ResponseEntity<PostDTO> getUserPost(
    @PathVariable Map<String, String> pathVariables
) {
    Long userId = Long.parseLong(pathVariables.get("userId"));
    Long postId = Long.parseLong(pathVariables.get("postId"));
    
    Post post = postService.findById(userId, postId);
    return ResponseEntity.ok(new PostDTO(post));
}

Комбинация Path и Query параметров

Path ID + Query фильтры

@GetMapping("/users/{id}/posts")
public ResponseEntity<List<PostDTO>> getUserPosts(
    @PathVariable Long id,
    @RequestParam(defaultValue = "0") int page,
    @RequestParam(defaultValue = "10") int size,
    @RequestParam(required = false) String sortBy
) {
    Pageable pageable = PageRequest.of(page, size, Sort.by(sortBy));
    List<Post> posts = postService.findByUserId(id, pageable);
    return ResponseEntity.ok(posts.stream().map(PostDTO::new).toList());
}

// GET /api/users/123/posts?page=0&size=20&sortBy=createdAt
// id = 123
// page = 0
// size = 20
// sortBy = "createdAt"

Обработка ошибок

Когда ID не найден

@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
    return userService.findById(id)
        .map(user -> ResponseEntity.ok(new UserDTO(user)))
        .orElseThrow(() -> new ResponseStatusException(
            HttpStatus.NOT_FOUND,
            "User with id " + id + " not found"
        ));
}

Глобальный exception handler

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(UserNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleUserNotFound(UserNotFoundException ex) {
        return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(new ErrorResponse("USER_NOT_FOUND", ex.getMessage()));
    }
    
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public ResponseEntity<ErrorResponse> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
        return ResponseEntity
            .status(HttpStatus.BAD_REQUEST)
            .body(new ErrorResponse("INVALID_ID_FORMAT", "ID должен быть числом"));
    }
}

Best Practices

  1. Используй правильный тип данных

    @PathVariable Long id  // для больших чисел
    @PathVariable UUID id  // для UUID
    @PathVariable String slug  // для slug'ов
    
  2. Валидируй ID

    @PathVariable @Positive Long id
    
  3. Именуй переменные понятно

    @GetMapping("/users/{userId}/posts/{postId}")  // ясно
    @GetMapping("/users/{id}/posts/{id}")  // ПЛОХО, конфликт имён!
    
  4. Обрабатывай ошибки gracefully

    • 404 если ID не найден
    • 400 если ID невалидный формат
  5. Используй RESTful соглашения

    • GET /api/users/123 — получить user
    • POST /api/users — создать user
    • PUT /api/users/123 — обновить user
    • DELETE /api/users/123 — удалить user

Резюме

@PathVariable — основной способ извлечения ID из пути. Spring автоматически:

  • Парсит значение из URL
  • Преобразует в нужный тип
  • Валидирует (если указано)
  • Передаёт в метод контроллера

Это один из самых используемых паттернов в REST API разработке.

Как достать id из пути в контроллере | PrepBro