Спроектировать и реализовать REST API для управления пользователем

1. Спроектировать и реализовать REST API для управления пользователем

Условие задачи:
Необходимо спроектировать REST API для управления пользователями.

API должно поддерживать:

  • создание пользователя;

  • получение пользователя по id;

  • получение списка пользователей;

  • обновление пользователя;

  • удаление пользователя.

Данные можно хранить в памяти.

Код:

public class User {

    private Long id;
    private String name;
    private String email;
    private Integer age;

    // getters / setters / constructors
}

Спойлеры к решению

Подсказки
💡 Используй @RestController и HTTP-методы POST, GET, PUT, DELETE.
💡 Логику работы с пользователями вынеси в @Service.
💡 Для хранения в памяти подойдёт ConcurrentHashMap.
💡 Для генерации идентификаторов можно использовать AtomicLong.
💡 Для входных данных удобно использовать отдельный DTO с валидацией.
💡 Если пользователь не найден, возвращай 404 Not Found.

Решение

DTO для входных данных:

public record UserRequest(
        @NotBlank String name,
        @NotBlank @Email String email,
        @Min(0) Integer age
) {
}

Модель:

public record User(
        Long id,
        String name,
        String email,
        Integer age
) {
}

Сервис:

@Service
public class UserService {

    private final Map<Long, User> users =
            new ConcurrentHashMap<>();

    private final AtomicLong idGenerator =
            new AtomicLong();

    public User create(UserRequest request) {
        long id = idGenerator.incrementAndGet();

        User user = new User(
                id,
                request.name(),
                request.email(),
                request.age()
        );

        users.put(id, user);

        return user;
    }

    public User getById(long id) {
        User user = users.get(id);

        if (user == null) {
            throw new NoSuchElementException(
                    "User not found: " + id
            );
        }

        return user;
    }

    public List<User> getAll() {
        return users.values().stream()
                .sorted(Comparator.comparing(User::id))
                .toList();
    }

    public User update(
            long id,
            UserRequest request
    ) {
        if (!users.containsKey(id)) {
            throw new NoSuchElementException(
                    "User not found: " + id
            );
        }

        User updated = new User(
                id,
                request.name(),
                request.email(),
                request.age()
        );

        users.put(id, updated);

        return updated;
    }

    public void delete(long id) {
        if (users.remove(id) == null) {
            throw new NoSuchElementException(
                    "User not found: " + id
            );
        }
    }
}

Контроллер:

@RestController
@RequestMapping("/api/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public User create(
            @Valid @RequestBody UserRequest request
    ) {
        return userService.create(request);
    }

    @GetMapping("/{id}")
    public User getById(
            @PathVariable long id
    ) {
        return userService.getById(id);
    }

    @GetMapping
    public List<User> getAll() {
        return userService.getAll();
    }

    @PutMapping("/{id}")
    public User update(
            @PathVariable long id,
            @Valid @RequestBody UserRequest request
    ) {
        return userService.update(id, request);
    }

    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void delete(
            @PathVariable long id
    ) {
        userService.delete(id);
    }
}

Обработка 404:

@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(NoSuchElementException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String handleNotFound(
            NoSuchElementException exception
    ) {
        return exception.getMessage();
    }
}

Получившийся API:

POST   /api/users
GET    /api/users/{id}
GET    /api/users
PUT    /api/users/{id}
DELETE /api/users/{id}

Для реального приложения хранилище в памяти можно заменить на JpaRepository, не меняя контракт контроллера.