8. Спроектировать и реализовать REST API для управления пользователем
Описание
Нужно спроектировать и реализовать REST API для управления пользователями:
- создать пользователя
- получить пользователя по id
- получить список пользователей
- обновить пользователя
- удалить пользователя
В рамках задания используем Spring Boot (без БД можно в памяти, но можно легко заменить на JPA).
Модель User:
public class User {
private Long id;
private String name;
private String email;
private Integer age;
// getters/setters/constructors
}
Спойлеры к решению
Подсказки
💡 Для REST API обычно делают
💡 Валидацию DTO удобно делать через
💡 Хранилище можно сделать на
💡 Для ошибок хорошо вернуть
💡 Для ручных тестов удобно подготовить
@RestController и маппинги POST/GET/PUT/DELETE.💡 Валидацию DTO удобно делать через
@Valid + @NotBlank/@Email/@Min.💡 Хранилище можно сделать на
ConcurrentHashMap<Long, User> + генерация id через AtomicLong.💡 Для ошибок хорошо вернуть
404 если не нашли, и 400 если валидация не прошла.💡 Для ручных тестов удобно подготовить
.http файлы (IDEA умеет выполнять).Решение
Ниже пример простого решения “в памяти” (без БД), но с корректными HTTP-статусами и DTO-валидацией.
- DTO + модель
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
public class UserDto {
@NotBlank
private String name;
@Email
@NotBlank
private String email;
@Min(0)
private Integer age;
// getters/setters
}
public class User {
private Long id;
private String name;
private String email;
private Integer age;
public User() {}
public User(Long id, String name, String email, Integer age) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
}
// getters/setters
}
- Service (в памяти)
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
@Service
public class UserService {
private final Map<Long, User> storage = new ConcurrentHashMap<>();
private final AtomicLong idSeq = new AtomicLong(0);
public User create(UserDto dto) {
long id = idSeq.incrementAndGet();
User user = new User(id, dto.getName(), dto.getEmail(), dto.getAge());
storage.put(id, user);
return user;
}
public User getById(long id) {
User user = storage.get(id);
if (user == null) {
throw new NoSuchElementException("User not found: " + id);
}
return user;
}
public List<User> getAll() {
return storage.values().stream()
.sorted(Comparator.comparing(User::getId))
.toList();
}
public User update(long id, UserDto dto) {
User existing = getById(id);
existing.setName(dto.getName());
existing.setEmail(dto.getEmail());
existing.setAge(dto.getAge());
storage.put(id, existing);
return existing;
}
public void delete(long id) {
if (storage.remove(id) == null) {
throw new NoSuchElementException("User not found: " + id);
}
}
}
- Controller
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.NoSuchElementException;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService service;
public UserController(UserService service) {
this.service = service;
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public User create(@Valid @RequestBody UserDto dto) {
return service.create(dto);
}
@GetMapping("/{id}")
public User getById(@PathVariable long id) {
return service.getById(id);
}
@GetMapping
public List<User> getAll() {
return service.getAll();
}
@PutMapping("/{id}")
public User update(@PathVariable long id, @Valid @RequestBody UserDto dto) {
return service.update(id, dto);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable long id) {
service.delete(id);
}
@ResponseStatus(HttpStatus.NOT_FOUND)
@ExceptionHandler(NoSuchElementException.class)
public String handleNotFound(NoSuchElementException e) {
return e.getMessage();
}
}
- .http файлы для ручных тестов API
users-create.http
POST http://localhost:8080/api/users
Content-Type: application/json
{
"name": "Alice",
"email": "alice@mail.com",
"age": 25
}
users-get.http
GET http://localhost:8080/api/users/1
GET http://localhost:8080/api/users
users-update.http
PUT http://localhost:8080/api/users/1
Content-Type: application/json
{
"name": "Alice Updated",
"email": "alice.updated@mail.com",
"age": 26
}
users-delete.http
DELETE http://localhost:8080/api/users/1