REST-контроллеры: @RestController и маршрутизация
Контроллер — это входная дверь REST API. Аннотации связывают URL и HTTP-метод с конкретным методом Java, а Spring сам превращает результат в JSON.
Суть: @RestController = @Controller + @ResponseBody. Метод возвращает объект — Spring через Jackson сериализует его в JSON и кладёт в тело ответа.
REST — это стиль построения API, где ресурсы (пользователи, заказы) адресуются через URL, а действия выражаются HTTP-методами: GET — прочитать, POST — создать, PUT — заменить, DELETE — удалить. Spring Boot делает написание таких API почти декларативным: вы расставляете аннотации, остальное фреймворк берёт на себя.
Базовый контроллер
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
public class UserController {
@GetMapping
public List<String> all() {
return List.of("Анна", "Борис");
}
@GetMapping("/{id}")
public String byId(@PathVariable Long id) {
return "Пользователь #" + id;
}
}
@RequestMapping("/api/users") на классе задаёт общий префикс. @GetMapping без аргумента означает «GET на /api/users», а @GetMapping("/{id}") — «GET на /api/users/7». Фигурные скобки в пути — это переменная, которую @PathVariable вытаскивает в параметр метода.
Как работает под капотом
При старте Spring сканирует контроллеры и строит карту маршрутов: для каждого метода запоминает URL-шаблон и HTTP-метод. Когда приходит запрос, DispatcherServlet ищет в этой карте подходящий метод (HandlerMapping), вызывает его, а возвращённый объект отдаёт сериализатору. Поскольку это @RestController, результат не ищется как шаблон страницы, а сериализуется в JSON через Jackson.
GET /api/users/7
|
v
DispatcherServlet
| ищет совпадение в карте маршрутов
v
/api/users/{id} -> UserController.byId(Long id)
| id = 7 (из @PathVariable)
v
возвращён объект -> Jackson -> JSON -> тело ответа
Смоделируем роутинг: по сути это поиск по таблице «(метод, шаблон) → функция»:
# Упрощённая карта маршрутов, как внутри DispatcherServlet
import re
routes = []
def route(method, pattern, handler):
regex = re.sub(r"\{(\w+)\}", r"(?P<\1>[^/]+)", pattern)
routes.append((method, re.compile("^" + regex + "$"), handler))
def all_users():
return ["Анна", "Борис"]
def user_by_id(id):
return "Пользователь #" + id
route("GET", "/api/users", lambda **kw: all_users())
route("GET", "/api/users/{id}", lambda **kw: user_by_id(kw["id"]))
def dispatch(method, path):
for m, rx, handler in routes:
match = rx.match(path)
if m == method and match:
return handler(**match.groupdict())
return "404 Not Found"
print(dispatch("GET", "/api/users")) # список
print(dispatch("GET", "/api/users/7")) # Пользователь #7
print(dispatch("GET", "/api/orders")) # 404
Запустите «Попробуй сам ▶». Spring делает примерно то же, но добавляет сериализацию, валидацию и обработку ошибок.
Частые ошибки
- Забыть @RestController. С обычным
@ControllerSpring попытается найти HTML-шаблон с именем строки-результата вместо отдачи JSON. - Дублирование маршрутов. Два метода на один и тот же URL и метод приведут к ошибке неоднозначного маппинга при старте.
- Глаголы в URL.
/getUser— антипаттерн. Ресурс — существительное (/users), действие выражает HTTP-метод.
Best practices
- Группируйте эндпоинты ресурса в один контроллер с общим
@RequestMapping. - Используйте множественное число существительных:
/api/users,/api/orders. - Держите контроллеры тонкими — вся логика в сервисе.
Итог: @RestController превращает методы класса в REST-эндпоинты. Spring строит карту маршрутов, находит метод по URL и HTTP-методу и сериализует результат в JSON. Дальше разберём, как принимать данные от клиента.
Закрепим главное
Контроллер — это переводчик между миром HTTP и миром Java-объектов, и важно не нагружать его ничем сверх этой роли. Каждый метод-эндпоинт должен читаться как короткая декларация: вот URL, вот HTTP-метод, вот какие данные принимаю, вот что возвращаю. Вся «настоящая работа» делегируется сервису. Такой контроллер легко читать, легко тестировать срезом @WebMvcTest и легко поддерживать.
Подумайте об именовании ресурсов заранее: хорошо спроектированное REST API выглядит предсказуемо, и опытный разработчик угадывает адреса, не глядя в документацию. Существительные во множественном числе, иерархия через вложенные пути (/api/users/7/orders), действия через HTTP-методы — это конвенции, которые экономят силы всей команде. Чем дисциплинированнее вы следуете им с первого дня, тем меньше технического долга накапливает API.