Я - человек простой. Вижу видео от Саши - ставлю лайк) спасибо за туториал)
@Edu-wi3il8 ай бұрын
Спасибо! Было полезно узнать про классы ProblemDetail и RestClient, как альтернатива RestTemplate
@akalavan53957 ай бұрын
Второй день, второе видео пройдено, всё очень интересно и познавательно.
@Devivl8 ай бұрын
Саш, как всегда, жирный лайк! Судя по гитхабу на выходе серьезный проект получается. Отличная практика. От души спасибо.
@tekkaruwer87897 ай бұрын
Очень крутое качество кода, зная все это, все равно очень полезно смотреть лекции, хотя бы просто для того, чтобы поучиться так грамотно писать) ❤
@МихаилМихаил-ф7уАй бұрын
Отличный материал! Спасибо огромное!
@Константин-ы9к7 ай бұрын
Наконец-то осилил до конца. Смотрю дальше. Отличный материал.
@itmaker18217 ай бұрын
Очень классный гайд, всё на высоте, спасибо
@Константин-ы9к8 ай бұрын
Отличное начало. Супер. Как всегда качественный профессиональный материал.
@ejatohvee3 ай бұрын
Спасибо вам большое за такие уроки. После получения в университете и на просторах интернета знаний очень помогает разложить все по полочкам, а также узнать что-то новое. Начинаю первый полностью свой проект по вашему курсу!
@АлександрЛапин-б7р4 ай бұрын
Огромное спасибо! Смотрится на одном дыхании!
@alekseizhitenev60204 ай бұрын
большое спасибо!) смотрится прям легко
@timur.k4 ай бұрын
Спасибо за отличный урок!
@vladislavchaplin8948 ай бұрын
ОЧень годно, спасибо большое, что делитесь
@artemv52268 ай бұрын
Эх, жаль, что цикла этого не было еще, когда ваял свой первый проект:) От прыжков по многочисленным граблям бы избавился. Спасибо за труды!
@АлександрТяпкин-п2ц8 ай бұрын
Спасибо за новый урок
@Lina-sl9js4 ай бұрын
Спасибо за видео! ) Хотела заметить, что для формирования ResponseEntity удобнее (как мне кажется) пользоваться готовым функционалом Spring Hateoas! ;)
@shurik_codesАй бұрын
Ну, такое)
@shluhogon_428 ай бұрын
Благодарю!
@ровойт8 ай бұрын
спасибо большое
@partiec60655 ай бұрын
Спасибо, братиш 🙂🤘
@webicode6 ай бұрын
СПАСИБО за труд !!!
@FrendlyFrend-g6j8 ай бұрын
Спасибо Александр и все таки URL и URI это не одно и тоже:)
@ГенрихАвдеев-ь9з8 ай бұрын
Привет. Отличный курс, большая работа проделана, виден системный подход. Большой лайк за труды. Вопрос по блоку возвращения ResponseEntity при обработке ошибок. Заметил, что статус ошибки заполняется два раза, сначала в ProblemDetail, потом как статус ResponseEntity, а problemDetail возвращается в body. В этом есть сакральный смысл (спрашиваю, т.к. есть ощущение, что просто так ничего в роликах не говорится и не делается)? Я у себя сделал вариант ResponseEntity .of(problemDetails) .build() Вроде работает ))
@shurik_codes8 ай бұрын
Да, действительно работает, я упустил этот момент
@romanovichihin24295 ай бұрын
Комент для продвижения
@aleksey27933 ай бұрын
8:00 - Когда речь идет про три разных варианта класса данных Product (для представления, базы данных и самого объекта), то нужны некие преобразователи из одного варианта в другой? Их реализовывать в контроллере и репозитории соответственно? Это получается что-то вроде адаптеров в гексагональной архитектуре?
@shurik_codes3 ай бұрын
По идее да, должны быть преобразователи, можно преобразования писать прямо в контроллере и репозитори, можно выносить в отдельные классы. Да, контроллер и репозиторий являются адаптерами в контексте гексагональной архитектуры
@o.sandman8 ай бұрын
Александр, спасибо за видео, с интересом посмотрел. Печалька, что такой полезный класс как ProblemDetail сделали только в 6 версии Spring, не могу его использовать - у нас на проекте 5-я версия)
@shurik_codes8 ай бұрын
Самостоятельно описать)
@_mapc876321 күн бұрын
Здраствуйте! 29:45 Не пойму смысл if (bindingResult.hasErrors()) { if (bindingResult instanceof BindException exception) { throw exception; } else { throw new BindException(bindingResult); } } Почему нельзя сократить до if (bindingResult.hasErrors()) { throw new BindException(bindingResult); }
@shurik_codes20 күн бұрын
В целом можно и так сделать, да, но зачем создавать новое исключение, если можно выбросить уже существующее?
@_mapc876320 күн бұрын
@@shurik_codes Понял, спасибо!
@alexandr60558 ай бұрын
Заметил у вас интересную штуку. Вот к примеру в методе мы не находим по id и в блоке if кидаем исключение. Затем отдельный метод "ловец" этого исключения. А не проще ли не кидать эксепшн, а просто в блоке иф делать обращение к методу "ловцу"? Как-то выглядит более прямой такая логика, нет?
@shurik_codes8 ай бұрын
Я немного ненаглядно показал, да, но предполагается, что в реальных условиях исключение может возникнуть в нескольких местах, и чтобы везде не писать обработку на месте, таким образом дублируя код, гораздо удобнее написать метод с @ExceptionHandler
@alexandr60558 ай бұрын
Понял, спасибо @@shurik_codes
@ji1ja8 ай бұрын
NoSuchElementException выбрасывается в нескольких местах, и будет странно если в одном мы будем выбрасывать в другом обращаться к методу, тем более, что оно выбрасывается в сервисе и мы оттуда не можем обращаться к контроллеру. Вообще по хорошему создать нужно какой-нибудь рест контроллер эдвайс и там уже хэндлить
@viewer_evgeniy7 ай бұрын
Я верно понимаю, что ResponseEntity по сути нужен только, если требуется кастомизация статуса? Если достаточно каких-то дефолтных (на успех это 200, а на ошибки не помню, что там спринг по умолчанию использует), то соответственно из методов контроллеров возвращаем просто нужный нам объект?
@shurik_codes7 ай бұрын
В целом да
@oleksandrhavryush92507 ай бұрын
Спасибо автору за отличный материал. У меня вопрос: объясните пожалуйста почему сервис и менеджер крутятся на разных портах, если мы в ямл файлах прописали один порт? Почему в постман мы посылаем запросы на порт 8081, а в браузере видим порт 8080? Вот этот момент до сих пор понять не могу.
@shurik_codes7 ай бұрын
В сервисе каталога порт - 8081 github.com/alex-kosarev/sc24/blob/SC24EP02-servlet-api-rest-service/catalogue-service/src/main/resources/application-standalone.yaml
@tusman4ik8 ай бұрын
❤)
@pavelgurevich40186 ай бұрын
Всем привет, подскажите почему в методе createProduct надо возвращать заголовок ?
@Hocorend6 ай бұрын
Спасибо за видео, уже становится сложнее всё запоминать из-за большой плотности информации, хоть и не впервой это вижу. Хоть уроков уже много выпущено, хотелось бы попросить озвучивать горячие клавиши, используемые при разработке, чтобы научиться так же быстро управляться с кодом, и прочие фишки IDEA . Ещё хотел уточнить, DTO не создаются для упрощения уроков и отсутствия работы с БД(на данный момент) или их создание устаревшая практика?
@shurik_codes6 ай бұрын
Исключительно для упрощения уроков, на практике DTO нужны
@artyomzolotoverkhov84685 ай бұрын
Чтобы лучше запоминать рекомендую повторять за Александром в своей IDE
@Hocorend5 ай бұрын
@@artyomzolotoverkhov8468 Это-то я несомненно делаю, просто смотреть вообще бестолку) Но по ему опыту, информация более менее железно укладывается только через пол года повторений
@СергейЗаря-х9ь2 ай бұрын
Добрый день, с чем связан выбор в пользу record, вместо стандартного dto?
@shurik_codes2 ай бұрын
Потому что record, на мой взгляд, идеальны для DTO
@trfguiikbfdyjkk5 ай бұрын
Здравствуйте, а зачем нужна проверка является ли BindingResult экземпляром BindException, нельзя сразу выбросить BindException(bindingResult)?
@shurik_codes5 ай бұрын
У BindingResult есть и другие реализации, не являющиеся исключением
@РусланСаматов-ы4с7 ай бұрын
Спасибо за ролик! С первого ролика не совсем понял, может кто нибудь объяснить почему мы используем NewPayload и UpdatePayload? Можно же вроде Product передавать?
@shurik_codes7 ай бұрын
Принцип единственной ответственности, по крайней мере одна из его трактовок. При создании и изменении объекта мы не задаём все свойства, из которых объект состоит. В добавок в разных операциях мы можем изменять разные наборы данных. На практике для каждой операции лучше иметь отдельный класс, описывающий полезную нагрузку.
@РусланСаматов-ы4с7 ай бұрын
@@shurik_codes Спасибо понял. А правильно ли я понимаю, что почти в таком же контексте(для не изменения сущности, и отделения ее) для таких целей при работе с базами данных используют DTO?
@shurik_codes7 ай бұрын
@@РусланСаматов-ы4с да
@non_holy62862 ай бұрын
Александр, привет! 22:40 тут не совсем понял в какой момент заполняется аргумент product на 27 строке, и в какой момент отработает метод на строке 21?
@non_holy62862 ай бұрын
Подебажил, как будто бы вызывается перед тем, как вызвать метод контроллера, но как это работает не понятно)
@shurik_codesАй бұрын
Метод getProduct (21 строка) вызывается перед findProduct (27 строка), его задача - добавить в модель товар, чтобы потом из модели передать товар в аргументы метода findProduct
@aleksey27934 ай бұрын
А как такие rest-сервисы масштабируются? Просто запускается еще один экземпляр, и нагрузка распределяется балансировщиком? Такая архитектура сервиса - хорошее решение для написания масштабируемых приложений?
@shurik_codes4 ай бұрын
Да, масштабирование происходит примерно по такой схеме. Архитектура тоже вполне пойдёт
@sdhfdsfh24138 ай бұрын
Привет, а как можно сделать, чтобы можно было скрывать левую боковую панель по бинду??
@shurik_codes8 ай бұрын
Alt+1
@ji1ja8 ай бұрын
Привет, спасибо за видос) Вопрос такой, почему просто не отлавливать в глобальном эксэпшн хэндлере ошибки валидации без BindingResult'а в контроллере?
@shurik_codes8 ай бұрын
А откуда взяться исключению?
@ji1ja8 ай бұрын
@@shurik_codes у спринга есть MethodArgumentNotValidException который экстендит bindexception и выбрасывается при фейле валидации
@rainrainov44955 ай бұрын
@@shurik_codes Добрый день. Через хэндлер отлавливается, просто нужно в настройках ProblemDetail подключить. К сожалению для ошибок валидации все равно придется в хендлере из BindingResult вытаскивать. Жаль стандарт не включает ошибки валидации и при тесте приходится заморачиваться с проверкой проперти, хотя можно просто расширить ProblemDetail, что мне кажется предпочтительней.
@hurricane-rus4 ай бұрын
@@shurik_codes Если у нас провалится валидация из Payload, вылетит MethodArgumentNotValidException - его как раз и будем перехватывать. Думаю, не стали заморачиваться с хэндлерами просто для экономии времени, а так конечно, ошибки (и тем более их обработку) обычно не ловят в контроллерах
@НатаСметанова7 ай бұрын
Добрый день! Спасибо за курс. У меня проблема - вроде делаю все также, но вылезает ошибка MissingPathVariableException: Required URI template variable 'productId' for method parameter type Integer is not present
@shurik_codes7 ай бұрын
Какой путь и как объявлен аргумент метода, получающий переменную пути?
@НатаСметанова7 ай бұрын
Появляется на страницах catalogue/products/list и catalogue/products/create, где в пути вообще нет productId
@shurik_codes7 ай бұрын
А в контроллере есть метод с аннотацией @ModelAttribute и аргументом, отмеченным аннотацией @PathVariable?
@НатаСметанова7 ай бұрын
@@shurik_codes Спасибо, поняла ))
@ровойт6 ай бұрын
а если у вас будет время можете показать какой-то реальный пример из жизни несложного проекта на спрингу
@SlevySoddik8 ай бұрын
вопрос по структуре методов контроллера с ветвлениями, зачем блок else если в блоке if есть return? верификация в контроллере не нарушает SRP?
@shurik_codes8 ай бұрын
Про метод с ветвлениями - не понял о каком методе идёт речь. Блок else затем, чтобы выполнить код, если условие в if не сработало. Валидация выполняется не в контроллере, а в валидаторе силами фреймворка, контроллер обрабатывает результат валидации, так что никакого нарушения SRP здесь нет.
@SlevySoddik8 ай бұрын
@@shurik_codes про if имел ввиду такую структуру: if(somePredicate){ return x; } return anotherX; таким образом else нет, и порядок обработки не теряется, но это, наверно, больше к код стайлу относится.
@shurik_codes8 ай бұрын
@@SlevySoddik а, ну так да, можно делать
@ИванГубарев-к4ь5 ай бұрын
Доброго времени суток! Подскажите, на JDK17 этот код должен работать?
@shurik_codes5 ай бұрын
Не тестировал, но вроде ничего из JDK 18+ в проекте не используется
@ЭдуардВолков-к7л5 ай бұрын
Привет, ты указываешь active profile - standalone на 59 минуте, как это сделать если версия community ?
@shurik_codes5 ай бұрын
spring.profiles.active в application.yml
@ЭдуардВолков-к7л5 ай бұрын
@@shurik_codes то есть в файле application-standalone.yaml прописать строчку spring: profile: active ------------- да?
@shurik_codes5 ай бұрын
@@ЭдуардВолков-к7л не, в файле application.yml, ну или при запуске mvn spring-boot:run -Dspring.profiles.active=standalone но это не точно
@GyMaNoiD-ql3wb5 ай бұрын
@@ЭдуардВолков-к7л С помощью терминала через cd заходите в директорию вашего сервиса("cd catalogue-service", например) и в терминале пишете: mvn spring-boot:run -D"spring-boot.run.profiles"=standalone
@svyatoiambrozii6 ай бұрын
Вам как профессиональному разработчику приятнее с REST или MVC работать? Где проще разработка выходит?)
@shurik_codes6 ай бұрын
С классическими веб-приложениями я работаю крайне редко (не знаю уж, к счастью или к сожалению), REST в моей практике занимает существенно больше времени. В целом мне без разницы с чем работать)
@denisskyter45268 ай бұрын
Я так понял restclient в manager-app это для взаимодействия с сервисом каталога что бы с клиента туда кидать запросы
@shurik_codes8 ай бұрын
да
@Пумпурумм24 күн бұрын
Классные видео, спору нет, но не разделяю шквал похвалы, тк задача автора не только всё детально показать, но и доходчиво объяснить. На мой взгляд данное видео местами сложновато для понимания новичками, и вызывает не мало вопросов, это на фоне других обучающих материалов по спрингу, где объясняют на примерах необходимого минимума.
@ИльяКалендарев-е8ж8 ай бұрын
Спасибо за видео. Но у меня есть вопрос) в Вашем примере объект UpdateProductPayload содержит всего два свойства. Предположим этих свойств гораздо больше и содержать новые данные для обновления могут не все т.е. допускается null значение. Как тогда оптимальней всего реализовать обновление объекта, может есть вариант, как избежать ручной проверки данных в каждом из свойств объекта?
@shurik_codes8 ай бұрын
Bean Validation)
@ИльяКалендарев-е8ж8 ай бұрын
@@shurik_codes Не очень понимаю, как это поможет если свойство может быть null, т.е. это значение разрешено, то при сохранении null заменит уже имеющееся значение в бд. А задача сохранить только те значения которые не null.
@shurik_codes8 ай бұрын
А, вот про что вопрос. Есть два варианта: 1. Описывать структуры данных для каждого варианта изменения, чтобы они содержали только те свойства, которые могут быть изменены 2. Для каждого варианта изменения описывать свой запрос к БД (специфично для SQL, JPA, Spring Data и т.д.)
@javac8 ай бұрын
Просто мысль вслух: можно попробовать @DynamicUpdate на сущность поставить, а при маппинге из payload в сущность null-ы пропускать (но это уже hibernate на борту подразумевается). Неизмененные поля в SQL не попадут (но потеряется перф, т.к. запрос динамический и не закешируется).
@bazilval6 ай бұрын
@@ИльяКалендарев-е8ж Посмотрите связку mapStruct и JsonNullable для этих целей. Если кратко, то в DTO, которые отвечают за создание и обновление сущности нужным полям нужно сделать тип JsonNullable (или не String). Это такая обёртка, которая позволит различать null явный, когда мы хотим, чтобы у сущности поле стало null и null от того, что мы не указали это поле в теле запроса. Далее при отправке запроса, в случае если это поле не будет указано в теле запроса, то его значение будет null, а если указали и явно указали ему стать null, то этот null будет зашит внутри обёртки После этого мы настраиваем mapStruct для того, чтобы такие поля корректно мапились при преобразовании DTO в объект Если интересно, могу скинуть учебный проект, где я такое реализовывал @shurik_codes также интересно ваше мнение про этот подход!
@ladamira34777 ай бұрын
У меня у одной что-ли в BindException нет метода getAllErrors()?
@ladamira34777 ай бұрын
а, все, исправила импорт)) но у меня в toList просто не собирается, требует collect(Collectors.toList()))
@shurik_codes7 ай бұрын
Это для JDK 17+, в примере используется JDK 21
@ladamira34777 ай бұрын
@@shurik_codes у меня 17. По идее должно работать. И ещё приложение при запуске каждый раз требует новый порт чтобы развернуться. В чем причина?
@shurik_codes7 ай бұрын
Что-то занимает этот порт, возможно, запущенный ранее сервис
@sermaz-blg7 ай бұрын
Приветствую. Что за плагин стоит и оставляет отметку simple (проценты)?
@shurik_codes7 ай бұрын
Code Complexity
@denisskyter45268 ай бұрын
А откуда у вас ultimate idea , с community не хочу ходить
@shurik_codes8 ай бұрын
Неравнодушные люди помогают обновлять подписку)
@Hocorend6 ай бұрын
На торрентах поищи, тебе же для обучения, никто штраф не предъявит, версию 2022 года точно можно найти
@romoshi7 ай бұрын
Не пойму почему не выводит продукт, на выхлопе получается "ID: 0, Название: , Описание: ", но в самом списке товаров id и title корректные. :(
@shurik_codes7 ай бұрын
Возможно, где-то есть ошибки, код к ролику: github.com/alex-kosarev/sc24/tree/SC24EP02-servlet-api-rest-service
@romoshi7 ай бұрын
@@shurik_codes Спасибо, буду разбираться!
@bolekrus6 ай бұрын
Catalague-service запускается без проблем, а Manager запускается один раз. Потом выключаешь, перезагружаешь программу, но всё равно ошибка Web server failed to start. Port 8080 was already in use.. Что за???
@shurik_codes6 ай бұрын
Ну, значит, приложение менеджера не выключается корректно
@odtourАй бұрын
59:47 при попытке перейти на localhost:8080/catalogue/products/list выдает ошибку: There was an unexpected error (type=Internal Server Error, status=500). java.lang.IllegalArgumentException: URI with undefined scheme ... at ag.selm.manager.client.RestClientProductsRestClient.findAllProducts(RestClientProductsRestClient.java:31) ~[classes/:na] at ag.selm.manager.controller.ProductsController.getProductsList(ProductsController.java:23) ~[classes/:na] и чё с этим делать непонятно
@shurik_codesАй бұрын
Посмотреть внимательно на настройки RestClient, судя по ошибке, URL не содержит схемы (http:)
@odtourАй бұрын
@@shurik_codes uri почему-то выходит вот такой: http/:localhost:8081/catalogue-api/products
@odtourАй бұрын
@@shurik_codes а где эти настройки найти?
@odtourАй бұрын
@@shurik_codes всё, нашел ошибку - было http//: вместо 😃