SC24EP02 Разработка REST-сервиса - Разработка проектов со Spring
Во втором ролике цикла я рассказываю об особенностях разработки REST-сервисов с помощью Spring WebMVC.
В цикле роликов "Разработка проектов со Spring" я рассказываю на простых примерах о процессе разработки веб-приложений и REST-сервисов на языке программирования Java с использованием экосистемы Spring. Данный цикл охватывает разработку классических и реактивных проектов, вопросы их сопровождения, такие как документация и мониторинг, адаптацию их к облачной инфраструктуре и процесс их развёртывания в Docker и Kubernetes.
Репозиторий проекта: github.com/alex-kosarev/sc24/...
Мои ресурсы:
- Сайт: alexkosarev.name
- Канал на KZread: / @shurik_codes
- Канал в Telegram: t.me/+TZCuO38vG3oqu_Jq
- Группа для обсуждений в Telegram: t.me/+UFAkw187WstX0wqy
- Паблик в VK: shurik.codes
- Канал в Дзене: dzen.ru/shurik_codes
- Канал на Rutube: rutube.ru/channel/24432001/
- Страница в Boosty: boosty.to/akosarev
Поддержать проект:
- Доны в VK: donut/shurik.codes
- Донаты в Boosty: boosty.to/akosarev/donate
- Через Tinkoff: www.tinkoff.ru/cf/4PEOiVCZQuS
#java #spring #rest #howto
Пікірлер: 92
Я - человек простой. Вижу видео от Саши - ставлю лайк) спасибо за туториал)
Спасибо, братиш 🙂🤘
Спасибо за новый урок
ОЧень годно, спасибо большое, что делитесь
Спасибо! Было полезно узнать про классы ProblemDetail и RestClient, как альтернатива RestTemplate
Очень классный гайд, всё на высоте, спасибо
СПАСИБО за труд !!!
Отличное начало. Супер. Как всегда качественный профессиональный материал.
Наконец-то осилил до конца. Смотрю дальше. Отличный материал.
Саш, как всегда, жирный лайк! Судя по гитхабу на выходе серьезный проект получается. Отличная практика. От души спасибо.
Второй день, второе видео пройдено, всё очень интересно и познавательно.
Благодарю!
спасибо большое
Очень крутое качество кода, зная все это, все равно очень полезно смотреть лекции, хотя бы просто для того, чтобы поучиться так грамотно писать) ❤
Комент для продвижения
Эх, жаль, что цикла этого не было еще, когда ваял свой первый проект:) От прыжков по многочисленным граблям бы избавился. Спасибо за труды!
Спасибо!
Спасибо Александр и все таки URL и URI это не одно и тоже:)
❤)
Александр, спасибо за видео, с интересом посмотрел. Печалька, что такой полезный класс как ProblemDetail сделали только в 6 версии Spring, не могу его использовать - у нас на проекте 5-я версия)
@shurik_codes
3 ай бұрын
Самостоятельно описать)
Привет. Отличный курс, большая работа проделана, виден системный подход. Большой лайк за труды. Вопрос по блоку возвращения ResponseEntity при обработке ошибок. Заметил, что статус ошибки заполняется два раза, сначала в ProblemDetail, потом как статус ResponseEntity, а problemDetail возвращается в body. В этом есть сакральный смысл (спрашиваю, т.к. есть ощущение, что просто так ничего в роликах не говорится и не делается)? Я у себя сделал вариант ResponseEntity .of(problemDetails) .build() Вроде работает ))
@shurik_codes
3 ай бұрын
Да, действительно работает, я упустил этот момент
Всем привет, подскажите почему в методе createProduct надо возвращать заголовок ?
Спасибо за видео, уже становится сложнее всё запоминать из-за большой плотности информации, хоть и не впервой это вижу. Хоть уроков уже много выпущено, хотелось бы попросить озвучивать горячие клавиши, используемые при разработке, чтобы научиться так же быстро управляться с кодом, и прочие фишки IDEA . Ещё хотел уточнить, DTO не создаются для упрощения уроков и отсутствия работы с БД(на данный момент) или их создание устаревшая практика?
@shurik_codes
Ай бұрын
Исключительно для упрощения уроков, на практике DTO нужны
@artyomzolotoverkhov8468
Ай бұрын
Чтобы лучше запоминать рекомендую повторять за Александром в своей IDE
@Hocorend
Ай бұрын
@@artyomzolotoverkhov8468 Это-то я несомненно делаю, просто смотреть вообще бестолку) Но по ему опыту, информация более менее железно укладывается только через пол года повторений
Заметил у вас интересную штуку. Вот к примеру в методе мы не находим по id и в блоке if кидаем исключение. Затем отдельный метод "ловец" этого исключения. А не проще ли не кидать эксепшн, а просто в блоке иф делать обращение к методу "ловцу"? Как-то выглядит более прямой такая логика, нет?
@shurik_codes
3 ай бұрын
Я немного ненаглядно показал, да, но предполагается, что в реальных условиях исключение может возникнуть в нескольких местах, и чтобы везде не писать обработку на месте, таким образом дублируя код, гораздо удобнее написать метод с @ExceptionHandler
@alexandr6055
3 ай бұрын
Понял, спасибо @@shurik_codes
@ji1ja
3 ай бұрын
NoSuchElementException выбрасывается в нескольких местах, и будет странно если в одном мы будем выбрасывать в другом обращаться к методу, тем более, что оно выбрасывается в сервисе и мы оттуда не можем обращаться к контроллеру. Вообще по хорошему создать нужно какой-нибудь рест контроллер эдвайс и там уже хэндлить
Спасибо автору за отличный материал. У меня вопрос: объясните пожалуйста почему сервис и менеджер крутятся на разных портах, если мы в ямл файлах прописали один порт? Почему в постман мы посылаем запросы на порт 8081, а в браузере видим порт 8080? Вот этот момент до сих пор понять не могу.
@shurik_codes
2 ай бұрын
В сервисе каталога порт - 8081 github.com/alex-kosarev/sc24/blob/SC24EP02-servlet-api-rest-service/catalogue-service/src/main/resources/application-standalone.yaml
Я верно понимаю, что ResponseEntity по сути нужен только, если требуется кастомизация статуса? Если достаточно каких-то дефолтных (на успех это 200, а на ошибки не помню, что там спринг по умолчанию использует), то соответственно из методов контроллеров возвращаем просто нужный нам объект?
@shurik_codes
2 ай бұрын
В целом да
Спасибо за ролик! С первого ролика не совсем понял, может кто нибудь объяснить почему мы используем NewPayload и UpdatePayload? Можно же вроде Product передавать?
@shurik_codes
2 ай бұрын
Принцип единственной ответственности, по крайней мере одна из его трактовок. При создании и изменении объекта мы не задаём все свойства, из которых объект состоит. В добавок в разных операциях мы можем изменять разные наборы данных. На практике для каждой операции лучше иметь отдельный класс, описывающий полезную нагрузку.
@user-nw2mu7vu3h
2 ай бұрын
@@shurik_codes Спасибо понял. А правильно ли я понимаю, что почти в таком же контексте(для не изменения сущности, и отделения ее) для таких целей при работе с базами данных используют DTO?
@shurik_codes
2 ай бұрын
@@user-nw2mu7vu3h да
Привет, спасибо за видос) Вопрос такой, почему просто не отлавливать в глобальном эксэпшн хэндлере ошибки валидации без BindingResult'а в контроллере?
@shurik_codes
3 ай бұрын
А откуда взяться исключению?
@ji1ja
3 ай бұрын
@@shurik_codes у спринга есть MethodArgumentNotValidException который экстендит bindexception и выбрасывается при фейле валидации
@rainrainov4495
3 күн бұрын
@@shurik_codes Добрый день. Через хэндлер отлавливается, просто нужно в настройках ProblemDetail подключить. К сожалению для ошибок валидации все равно придется в хендлере из BindingResult вытаскивать. Жаль стандарт не включает ошибки валидации и при тесте приходится заморачиваться с проверкой проперти, хотя можно просто расширить ProblemDetail, что мне кажется предпочтительней.
а если у вас будет время можете показать какой-то реальный пример из жизни несложного проекта на спрингу
Спасибо за видео. Но у меня есть вопрос) в Вашем примере объект UpdateProductPayload содержит всего два свойства. Предположим этих свойств гораздо больше и содержать новые данные для обновления могут не все т.е. допускается null значение. Как тогда оптимальней всего реализовать обновление объекта, может есть вариант, как избежать ручной проверки данных в каждом из свойств объекта?
@shurik_codes
3 ай бұрын
Bean Validation)
@user-ox1hp7sm4o
3 ай бұрын
@@shurik_codes Не очень понимаю, как это поможет если свойство может быть null, т.е. это значение разрешено, то при сохранении null заменит уже имеющееся значение в бд. А задача сохранить только те значения которые не null.
@shurik_codes
3 ай бұрын
А, вот про что вопрос. Есть два варианта: 1. Описывать структуры данных для каждого варианта изменения, чтобы они содержали только те свойства, которые могут быть изменены 2. Для каждого варианта изменения описывать свой запрос к БД (специфично для SQL, JPA, Spring Data и т.д.)
@javac
3 ай бұрын
Просто мысль вслух: можно попробовать @DynamicUpdate на сущность поставить, а при маппинге из payload в сущность null-ы пропускать (но это уже hibernate на борту подразумевается). Неизмененные поля в SQL не попадут (но потеряется перф, т.к. запрос динамический и не закешируется).
@bazilval
2 ай бұрын
@@user-ox1hp7sm4o Посмотрите связку mapStruct и JsonNullable для этих целей. Если кратко, то в DTO, которые отвечают за создание и обновление сущности нужным полям нужно сделать тип JsonNullable (или не String). Это такая обёртка, которая позволит различать null явный, когда мы хотим, чтобы у сущности поле стало null и null от того, что мы не указали это поле в теле запроса. Далее при отправке запроса, в случае если это поле не будет указано в теле запроса, то его значение будет null, а если указали и явно указали ему стать null, то этот null будет зашит внутри обёртки После этого мы настраиваем mapStruct для того, чтобы такие поля корректно мапились при преобразовании DTO в объект Если интересно, могу скинуть учебный проект, где я такое реализовывал @shurik_codes также интересно ваше мнение про этот подход!
вопрос по структуре методов контроллера с ветвлениями, зачем блок else если в блоке if есть return? верификация в контроллере не нарушает SRP?
@shurik_codes
3 ай бұрын
Про метод с ветвлениями - не понял о каком методе идёт речь. Блок else затем, чтобы выполнить код, если условие в if не сработало. Валидация выполняется не в контроллере, а в валидаторе силами фреймворка, контроллер обрабатывает результат валидации, так что никакого нарушения SRP здесь нет.
@SlevySoddik
3 ай бұрын
@@shurik_codes про if имел ввиду такую структуру: if(somePredicate){ return x; } return anotherX; таким образом else нет, и порядок обработки не теряется, но это, наверно, больше к код стайлу относится.
@shurik_codes
3 ай бұрын
@@SlevySoddik а, ну так да, можно делать
Привет, а как можно сделать, чтобы можно было скрывать левую боковую панель по бинду??
@shurik_codes
3 ай бұрын
Alt+1
Приветствую. Что за плагин стоит и оставляет отметку simple (проценты)?
@shurik_codes
2 ай бұрын
Code Complexity
Здравствуйте, а зачем нужна проверка является ли BindingResult экземпляром BindException, нельзя сразу выбросить BindException(bindingResult)?
@shurik_codes
5 күн бұрын
У BindingResult есть и другие реализации, не являющиеся исключением
Я так понял restclient в manager-app это для взаимодействия с сервисом каталога что бы с клиента туда кидать запросы
@shurik_codes
3 ай бұрын
да
Добрый день! Спасибо за курс. У меня проблема - вроде делаю все также, но вылезает ошибка MissingPathVariableException: Required URI template variable 'productId' for method parameter type Integer is not present
@shurik_codes
2 ай бұрын
Какой путь и как объявлен аргумент метода, получающий переменную пути?
@user-vs6ce8vq2h
2 ай бұрын
Появляется на страницах catalogue/products/list и catalogue/products/create, где в пути вообще нет productId
@shurik_codes
2 ай бұрын
А в контроллере есть метод с аннотацией @ModelAttribute и аргументом, отмеченным аннотацией @PathVariable?
@user-vs6ce8vq2h
2 ай бұрын
@@shurik_codes Спасибо, поняла ))
Доброго времени суток! Подскажите, на JDK17 этот код должен работать?
@shurik_codes
8 күн бұрын
Не тестировал, но вроде ничего из JDK 18+ в проекте не используется
Привет, ты указываешь active profile - standalone на 59 минуте, как это сделать если версия community ?
@shurik_codes
Ай бұрын
spring.profiles.active в application.yml
@user-fs7qm9tw2d
Ай бұрын
@@shurik_codes то есть в файле application-standalone.yaml прописать строчку spring: profile: active ------------- да?
@shurik_codes
Ай бұрын
@@user-fs7qm9tw2d не, в файле application.yml, ну или при запуске mvn spring-boot:run -Dspring.profiles.active=standalone но это не точно
@GyMaNoiD-ql3wb
27 күн бұрын
@@user-fs7qm9tw2d С помощью терминала через cd заходите в директорию вашего сервиса("cd catalogue-service", например) и в терминале пишете: mvn spring-boot:run -D"spring-boot.run.profiles"=standalone
А откуда у вас ultimate idea , с community не хочу ходить
@shurik_codes
3 ай бұрын
Неравнодушные люди помогают обновлять подписку)
@Hocorend
Ай бұрын
На торрентах поищи, тебе же для обучения, никто штраф не предъявит, версию 2022 года точно можно найти
Вам как профессиональному разработчику приятнее с REST или MVC работать? Где проще разработка выходит?)
@shurik_codes
2 ай бұрын
С классическими веб-приложениями я работаю крайне редко (не знаю уж, к счастью или к сожалению), REST в моей практике занимает существенно больше времени. В целом мне без разницы с чем работать)
Не пойму почему не выводит продукт, на выхлопе получается "ID: 0, Название: , Описание: ", но в самом списке товаров id и title корректные. :(
@shurik_codes
2 ай бұрын
Возможно, где-то есть ошибки, код к ролику: github.com/alex-kosarev/sc24/tree/SC24EP02-servlet-api-rest-service
@romoshi
2 ай бұрын
@@shurik_codes Спасибо, буду разбираться!
Catalague-service запускается без проблем, а Manager запускается один раз. Потом выключаешь, перезагружаешь программу, но всё равно ошибка Web server failed to start. Port 8080 was already in use.. Что за???
@shurik_codes
Ай бұрын
Ну, значит, приложение менеджера не выключается корректно
У меня у одной что-ли в BindException нет метода getAllErrors()?
@ladamira3477
2 ай бұрын
а, все, исправила импорт)) но у меня в toList просто не собирается, требует collect(Collectors.toList()))
@shurik_codes
2 ай бұрын
Это для JDK 17+, в примере используется JDK 21
@ladamira3477
2 ай бұрын
@@shurik_codes у меня 17. По идее должно работать. И ещё приложение при запуске каждый раз требует новый порт чтобы развернуться. В чем причина?
@shurik_codes
2 ай бұрын
Что-то занимает этот порт, возможно, запущенный ранее сервис