Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Вопросы по Spring MVC, жду советов и предложений. 
:(
    Опции темы
garbuz
Дата 8.5.2009, 18:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Изучаю SpringMVC, пишу некое подобие блога.

1) Есть PostController, который выбирает из базы пост и комменты к нему, затем все это отображается на страничке. Там же есть поле для добавления комментария. Как лучше организовать этот процесс? а именно:

- Должен ли быть контроллер, который обрабатывает комментарии обязательно FormController, так как сам комментарий мы получаем из формы. Или же это может быть обычный AbstractController, а текст комментария просто получать из запроса. (Валидацию можно возложить на javascript)

- Когда сабмичу комментарий нужно как-то указать к какому посту этот комментарий принадлежит, хотя мы явно понимаем какому, но это же надо как-то указать и приложению. Тут я вижу два пути -  передавать сам объект типа Post с jsp странички, который мы раньше получили и отобразили, или же передавать id этого поста и уже в контроллере по id выбирать пост и назначать его комментарию. Если втрой вариант, то id можно передать hidden полем в форме, а если первый, как то как передать объект Post? Просто в скриплете положить в запрос или в сессию? Может есть более красивое решение? Может есть какие теги спринговые полезные или еще есть вариант?

2) Добавление комментариев лучше производить в отдельном контроллере или же в PostControlelr?

3) Предположим мы добавили комментарий, если это произошло в отдельном контроллере (CommentController), то как нам потом форварднуться на PostController, чтобы заново выбрать и отобразить уже пост с обновленными комментариями?      

4) Если форварднемся на PostController, то опять будет происходить выборка того же поста и тех же комментариев + 1 новый. Не есть хорошо. Можно конечно же деалть через аджакс, но аджакс пока прикручивать я не тороплюсь.

Короче вот такая цепочка вопросов.  smile 
PM MAIL   Вверх
Vasay
Дата 8.5.2009, 18:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2097
Регистрация: 8.3.2006

Репутация: 18
Всего: 73



garbuz

Цитата

(Валидацию можно возложить на javascript)


ИМХО Плохая идея. Валидация ВСЕГДА должна быть на стороне сервера и опционально на клиенте, поскольку, js на клинте может работать не корректно, или просто не работать - откуда Вы знаете с какого устройства будут смотреть Ваш блог.

Кроме того, любой "кулхацкер" сможет подправив код страницы, обойти Вашу JS валидацию.

Цитата

2) Добавление комментариев лучше производить в отдельном контроллере или же в PostControlelr?


ИМХО - в отдельном.

Цитата

- Когда сабмичу комментарий нужно как-то указать к какому посту этот комментарий принадлежит, хотя мы явно понимаем какому, но это же надо как-то указать и приложению. Тут я вижу два пути -  передавать сам объект типа Post с jsp странички, который мы раньше получили и отобразили, или же передавать id этого поста и уже в контроллере по id выбирать пост и назначать его комментарию. Если втрой вариант, то id можно передать hidden полем в форме, а если первый, как то как передать объект Post? Просто в скриплете положить в запрос или в сессию? Может есть более красивое решение? Может есть какие теги спринговые полезные или еще есть вариант?


Думаю, id в хидден поле. 


Цитата

4) Если форварднемся на PostController, то опять будет происходить выборка того же поста и тех же комментариев + 1 новый. Не есть хорошо. Можно конечно же деалть через аджакс, но аджакс пока прикручивать я не тороплюсь.


ИМХО, это тот случай, когда грамотная реализация ajax будет лучшим выбором, а пока - "выборка того же поста и тех же комментариев + 1 новый"


--------------------
Придумать идеальную защиту от дурака невозможно, дураки, наудивление, изобретательны.
PM MAIL   Вверх
garbuz
Дата 8.5.2009, 18:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Vasay, спасибо за ответы!

Цитата(Vasay @  8.5.2009,  18:35 Найти цитируемый пост)
ИМХО Плохая идея. Валидация ВСЕГДА должна быть на стороне сервера и опционально на клиенте,

Полностью согласен, но на первое время думал обойтись без серверной валидации.


Цитата(Vasay @  8.5.2009,  18:35 Найти цитируемый пост)
Думаю, id в хидден поле. 

Смотрите, если же передавать не id а объект Post, то этот объект можно потом передать в Post контроллер и тогда нам останется подтацить только комменты. Т.е. что лучше будет? Потаскать объект через контроллеры или сделать + 1 запрос к базе?  Отсюда вытекает вопрос, который есть в первом моем посте - как передать объект правильнее и как потом с этим объектом форварднуться на другой контроллер?

PM MAIL   Вверх
garbuz
Дата 13.5.2009, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Следующий вопрос.
Создание нового поста лучше производить в отдельном контроллере или же в этом же? Большинство небольших примеров что я видел, работали по правилу один запрос - один контроллер. Правильно ли это? Я думаю сейчас делать все в одном контроллере, например в PostController будет работа с потами (типа просмотр, создание, удаление, редактирование и т.п.) Рулить собираюся всем через параметр action, в контроллере перебираю его через if и в зависимости от значения выполняю действия. Такой подход нормальный? Или же лепить много контроллеров на каждое действие?
PM MAIL   Вверх
goodday1941
Дата 13.5.2009, 16:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



id в хиден поля можно не класть

использую к примеру SimpleFormController
и добавив атрибут в настройке контроллера sessionForm = true (в таком случае контроллер в formBackingObject будет заходить только один раз, но там есть подводные камни)

и еще пытайтесь всегда использовать для форм SimpleFormController (ну и еже с ними Wizard и тд, но не AbstractController и MultiactionController) в конечном итоге это очень сильно сэкономит время smile

Добавлено через 2 минуты и 36 секунд
валидацию форм тоже проводите в отдельных классах имплементирующих интерфейс Validator

Добавлено через 4 минуты и 1 секунду
Цитата(garbuz @  8.5.2009,  18:57 Найти цитируемый пост)
Полностью согласен, но на первое время думал обойтись без серверной валидации.

зря... делается она придельно просто и красиво с помощью спринга


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 13.5.2009, 17:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



goodday1941, спасибо за советы, учту. А вот что на счет последнего вопроса?
Цитата

Создание нового поста лучше производить в отдельном контроллере или же в этом же? Большинство небольших примеров что я видел, работали по правилу один запрос - один контроллер. Правильно ли это? Я думаю сейчас делать все в одном контроллере, например в PostController будет работа с потами (типа просмотр, создание, удаление, редактирование и т.п.) Рулить собираюся всем через параметр action, в контроллере перебираю его через if и в зависимости от значения выполняю действия. Такой подход нормальный? Или же лепить много контроллеров на каждое действие? 

PM MAIL   Вверх
goodday1941
Дата 13.5.2009, 17:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



я делаю обычно так:

форма - 100% SimpleFormController с Validator (даже если нечего валидировать smile, хотя что валидировать есть всегда)
что то визардовое (пошаговое) - соответственно WizardController
ну и далее действие относящиеся к одной сущности (например показать список пользователей, удалить пользователя) - MultiactionController
AbstractController использую очень редко но бывают ситуации


рулить через параметр экшин ИМХО недопустимо....  if - else это из розряда функционального программирования ) легко запутаться
лучше иметь 10 простых классов по 100 строк чем один на 1000


Это сообщение отредактировал(а) goodday1941 - 13.5.2009, 17:41


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 13.5.2009, 18:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



goodday1941, еще раз спасибо за советы. Про formController приму к сведению, даже наверно постараюсь переделать то, что уже есть.
Недавно читал про MultiactionController. Там идея понятна, если конечно сведения из моей книги не устарели smile
Допустим будет что-то типа такого 
Код

public class PostController extends MultiActionController{
     private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }

    public ModelAndView create(request, response){
    // тут создаем новый пост 
    }

    public ModelAndView delete(request, response){
          // тут получаем id поста и удаляем его
    } 
}

В случае отображения, как я понимаю урл должен быть типа /post.htm?action=show&postId=x
В случае create, как я понимаю урл должен быть типа /post.htm?action=create
В случае delete, как я понимаю урл должен быть типа /post.htm?action=delete&postId=x

Естественно, что действие удаления поста будет доступно только для админов, но обычный пользователь тоже сможет написать такой урл и отправить его серверу и тогда произойдет удаление поста, который не следует удалять. Как быть в такой ситуации?

Добавлено через 7 минут
И как быть если у нас один котроллер должен быть  Multiaction, а так же должен обрабатывать данные от формы? От какого екстендиться предпочтительнее?
PM MAIL   Вверх
goodday1941
Дата 13.5.2009, 19:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
В случае отображения, как я понимаю урл должен быть типа /post.htm?action=show&postId=x
В случае create, как я понимаю урл должен быть типа /post.htm?action=create
В случае delete, как я понимаю урл должен быть типа /post.htm?action=delete&postId=x

нет

урл зависит от того что прописано в маппинге...
например при SimpleUrlHandlerMapping будем иметь урлы = названию метода
тоесть 
     
Код

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
            <props>
                <prop key="/test/*">multiController</prop>
              </props>
        </property>
    </bean>


так же можно явно указывать какой урл соответствует какому методу, думаб в вашей книге это есть ) если конечно ваша книга называется Spring in Action )

Добавлено через 3 минуты и 26 секунд
Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
И как быть если у нас один котроллер должен быть  Multiaction, а так же должен обрабатывать данные от формы? От какого екстендиться предпочтительнее? 

тут будет два контроллера


Цитата(garbuz @  13.5.2009,  18:24 Найти цитируемый пост)
Естественно, что действие удаления поста будет доступно только для админов, но обычный пользователь тоже сможет написать такой урл и отправить его серверу и тогда произойдет удаление поста, который не следует удалять. Как быть в такой ситуации?

почитай про asegi - это приблуда на спринг для аутентификации, 
ну или более простой вариант повесить фильтр на урл доступ к которому имеют только одмины

Добавлено через 5 минут
фильтр проверяет залогинен ли пользователь если нет то редиректит запрос на страницу с логином


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 13.5.2009, 19:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(goodday1941 @  13.5.2009,  19:19 Найти цитируемый пост)
так же можно явно указывать какой урл соответствует какому методу, думаб в вашей книге это есть ) если конечно ваша книга называется Spring in Action )

А можно примерчик, если есть конечно. Книга Spring in Action, но первое издание. Посмотрел во втором, там про Multiaction не написано вообще, точнее написано, а примера нет.
PM MAIL   Вверх
garbuz
Дата 13.5.2009, 23:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Итак. С самого начала начал лепить практически везде abstract контроллеры. Ну что же, первый блин как всегда. Решил все переделать с самого начала, но сперва решил еще раз посоветоваться. Вот что я решил:
1) HomePageController ext AbstractController - выводит на главную страницу все посты и информацию к ним (дату, теги, автора).

2) PostController ext MultiactionController - показывает конкретный пост с коментариями и удаляет посты, принимает в качестве параметра postId.

3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

4) CommentController ext SimpleFormController - создает новый комментарий, который приходит из формы, хотя в форме всего одно поле. (думаю что удаление и редактирование комментариев не нужно вовсе, хотя удаление может быть...)

ну что? Кто что думает на счет таког подхода?

Теперь осталось разобраться как всем эти рулить.

Нашел в оф доках  ParameterMethodNameResolver, который выбирает метод в зависимости от значения указанного параметра, например action=create, action=edit.
Так же нашел PropertiesMethodNameResolver, который, как я понял, биндит урл на конкретный метод. 
Там же в обоих примерах упоминается некий делегат, которому, как я понял, мы можем делегировать нек методы. Вопрос для чего? Не проще ли все объявленные методы в контроллере реализовать? Зачем еще делегат?

Короче вот как-то так, жду ответов smile Заранее спасибо!  smile 

PM MAIL   Вверх
_and
Дата 14.5.2009, 05:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java-developer
*


Профиль
Группа: Участник
Сообщений: 139
Регистрация: 6.8.2004
Где: Новокузнецк

Репутация: 1
Всего: 4



garbuz, советую посмотреть на сэмплы спринга типа jpetstore и остальные(обычно лежат в архиве спринга) половина вопросов отпадут сами собой

Цитата(garbuz @  14.5.2009,  03:33 Найти цитируемый пост)
3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

этого точно не стоит делать, каждое действие должно лежать в своем классе


Цитата(garbuz @  14.5.2009,  03:33 Найти цитируемый пост)
4) CommentController ext SimpleFormController - создает новый комментарий, который приходит из формы, хотя в форме всего одно поле. (думаю что удаление и редактирование комментариев не нужно вовсе, хотя удаление может быть...)

можно добавить поля имя автора камента и мыло. Удаления камента сделаь для админа и автора поста

Цитата(garbuz @  13.5.2009,  23:40 Найти цитируемый пост)
А можно примерчик, если есть конечно. Книга Spring in Action, но первое издание. Посмотрел во втором, там про Multiaction не написано вообще, точнее написано, а примера нет.

взято из сэмпла по спрингу petclinic
Код

<!-- маппинг -->
<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/welcome.htm">clinicController</prop>
                <prop key="/vets.htm">clinicController</prop>
                <prop key="/owner.htm">clinicController</prop>
            </props>
        </property>
    </bean>
<!-- контроллер -->
<bean id="clinicController" class="org.springframework.samples.petclinic.web.ClinicController">
        <property name="methodNameResolver" ref="clinicControllerResolver"/>
        <property name="clinic" ref="clinic"/>
    </bean>
<!-- резолвер для мультиакшн контроллера -->
    <bean id="clinicControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
        <property name="mappings">
        <props>
            <prop key="/welcome.htm">welcomeHandler</prop>
            <prop key="/vets.htm">vetsHandler</prop>
            <prop key="/owner.htm">ownerHandler</prop>
        </props>
        </property>
    </bean>

PM MAIL ICQ Jabber   Вверх
goodday1941
Дата 14.5.2009, 11:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



Цитата(garbuz @  13.5.2009,  23:33 Найти цитируемый пост)
Нашел в оф доках  ParameterMethodNameResolver, который выбирает метод в зависимости от значения указанного параметра, например action=create, action=edit.

этого лучше не использовать.. нужно ж и про SEO думать, а поисковикам урлы с параметрами не нравятся

Цитата(garbuz @  13.5.2009,  23:33 Найти цитируемый пост)
3) CreatePostController ext SimpleFormController - создает и редактирует пост, так как там тоже все на формах. А возможно в этом контроллере проводит вякую валидацию и создание объекта, а потом с этим объектом редиректиться на PostController и там уже в соответствующих метода записывать объект в базу? Или так не стоит делать?

валидацью можно проводить и в этом классе но лучше написать отдельный класс валидатор.. в интернете примеров валом есть как это делается
создание объекта - что имеется ввиду?


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 14.5.2009, 11:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(goodday1941 @  14.5.2009,  11:07 Найти цитируемый пост)

этого лучше не использовать.. нужно ж и про SEO думать, а поисковикам урлы с параметрами не нравятся

Значит лучше использовать PropertiesMethodNameResolver?

Со всем остальным пока буду разбираться.
PM MAIL   Вверх
goodday1941
Дата 14.5.2009, 12:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



Цитата(garbuz @  14.5.2009,  11:51 Найти цитируемый пост)

Значит лучше использовать PropertiesMethodNameResolver?

я использую SimpleUrlHandlerMapping и не указую резолвера когда создаю спринг бины

тогда например название метода в MultiactionController'е будет соответствовать урлу, а для остальных что пропишем при маппинге то и получим

но это дело вкуса многие прописывают для мультиэкшн контроллера PropertiesMethodNameResolver


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 14.5.2009, 15:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Итак господа, смотрим что получилось. Пинаем, ругаем советуем.

Код

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="home" class="ru.blog.web.controllers.HomePageController">
        <property name="daoFacade" ref="daoFacade"/>
    </bean>

    <bean name="post" class="ru.blog.web.controllers.PostController">
        <property name="daoFacade" ref="daoFacade"/>
        <property name="methodNameResolver" ref="postControllerResolver"/>
    </bean>

   
    <bean id="postControllerResolver" class="org.springframework.web.servlet.mvc.multiaction.PropertiesMethodNameResolver">
        <property name="mappings">
        <props>
            <prop key="/showPost.htm">show</prop>
            <prop key="/newPost.htm">create</prop>
            <prop key="/editPost.htm">edit</prop>
        </props>
        </property>
    </bean>

    <bean id="simpleUrlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/home.htm">home</prop>
                <prop key="/showPost.htm">post</prop>
                <prop key="/newPost.htm">post</prop>
                <prop key="/editPost.htm">post</prop>
                           
            </props>
        </property>
    </bean>



    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"></property>
        <property name="prefix" value=""></property>
        <property name="suffix" value=".jsp"></property>
    </bean>

</beans>



Получаем все посты и отображаем их на главной.
Код

public class HomePageController extends AbstractController {

    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }

    protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<Post> posts =  daoFacade.getPostDAO().getAll();
        return new ModelAndView("home", "posts", posts);
    }
}



Ниже представлена вьюха главной страницы. Интересует степерь убогости определения залогинен ли пользователи или нет, а так же то место, где я создаю ссылку на редактирование поста для его автора. Так правильно делать или же есть более красивое и правильное решение?

home.jsp
Код

<%@ include file="include.jsp" %>
<html>
<head><title>Home page</title></head>
<body>
<div style="width:900px">
    <%@ include file="header.jsp" %>

    <%
        boolean loggedIn = false;
        if (request.getUserPrincipal() != null){
            loggedIn = true;
        }
        pageContext.setAttribute("loggedIn", new Boolean(loggedIn));
    %>

    <table border="1">

        <c:forEach items="${posts}" var="post">
            <tr>
                <td colspan="2"><h4><c:out value="${post.postName}"/></h4>
                    <c:if test="${loggedIn}">
                        <c:set var="author" value="<%=request.getUserPrincipal().getName()%>"/>
                        <c:if test="${post.author.login eq author}">
                            <a href="editPost.htm?postId=${post.postId}">edit</a>
                        </c:if>
                    </c:if>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <c:out value="${post.preview}"/> <br><br>
                    <a href="showPost.htm?postId=${post.postId}">Read more >></a>
                </td>
            </tr>
            <tr>
                <td><c:out value="${post.author.login}"/></td>
                <td><c:out value="${post.createdDate}"/></td>

            </tr>
            <tr>
                <td colspan="2">
                    <c:forEach items="${post.tags}" var="tag">
                        <c:out value="${tag.tagName}"/>
                    </c:forEach>
                </td>
            </tr>
        </c:forEach>

    </table>
</div>

</body>
</html>




Код

public class PostController extends MultiActionController {

    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }


    public ModelAndView show(HttpServletRequest request, HttpServletResponse response) {
        String postId = request.getParameter("postId");
        Post post = daoFacade.getPostDAO().getById(Long.parseLong(postId));
        List<Comment> comments = daoFacade.getCommentDAO().getFromPost(post);
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("post", post);
        model.put("comments", comments);
        return new ModelAndView("post", model);
    }


    public ModelAndView create(HttpServletRequest request, HttpServletResponse response) {
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("categories", categories);
        return new ModelAndView("newPost", model);
    }

    public ModelAndView edit(HttpServletRequest request, HttpServletResponse response) {
        String postId = request.getParameter("postId");
        Post post = daoFacade.getPostDAO().getById(Long.parseLong(postId));
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("post", post);
        model.put("categories", categories);
        return new ModelAndView("editPost", model);
    }


}


Сейчас PostController занимается тем, что показывает конкретный пост, выбирает данные для создания нового поста и переправляет нас на нужную вьюху, выбирает данные для редактирования поста и перенаправляет нас на нужную вьюху. Контроллеры, которые будут создавать посты и редактировать, еще не готовы. Пока в FormController не разобрался, как-то примеров внятных не получается найти. Может кто выложит и объяснит что к чему?

И еще вопрос, который уже был выше. Итак, пользователь у нас залогинен, ему отображается ссылочка для редактирования поста типа 
http://localhost:8080/blog/editPost.htm?postId=1, он там подредактирует, отсабмитит и все окей. Если пользователь не залогинен, тогда он отсылается на форму логина, это делает сам томкат. Но что делать если пользователь залогинен, но не является автором поста но каким-то чудом узнал такую ссылку и прошел по ней. Открывается та же форма редактирования. Выше предлагались решения, пока решил остановиться на фильтре. Как в фильтре проверить может ли этот пользователь править пост или нет?


PM MAIL   Вверх
goodday1941
Дата 14.5.2009, 16:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 709
Регистрация: 16.7.2006
Где: Украина, Киев

Репутация: нет
Всего: 9



времени нет смотреть, но то что бросилось в глаз:

 <%@ include file="header.jsp" %>

скриплеты - зло - по возможности попробуй отказаться от них раз и навсегда

используй jstl теги

используй теги Spring MVC для форм которые пишуться под SimpleFormController


--------------------
SCJP 6
PM MAIL ICQ Skype GTalk   Вверх
garbuz
Дата 14.5.2009, 17:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(goodday1941 @  14.5.2009,  16:37 Найти цитируемый пост)
<%@ include file="header.jsp" %>

скриплеты - зло - по возможности попробуй отказаться от них раз и навсегда

используй jstl теги

Окей, буду стараться.


Как на счет остальных вопросов в предыдущем посте.
PM MAIL   Вверх
garbuz
Дата 15.5.2009, 15:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Разбираюсь с simpleFormController. Вот цитата из Spring in Action
Цитата

Form controllers take the concept of command controllers a step further, by adding functionality to display a form when an HTTP GET request is received and process the form when an HTTP POST is received.

Значит моя писанина выше с PostController не верна! Ведь именно он сейчас отображает вьюхи с формами! Так же это значит, что мой PostController не должен быть никаким multiAction контреллером, а должен лишь выполнять одно действие - выбирать пост и отображать его.

Итак, что я понял из прочитанного. Если где-то ошибусь - прошу поправить.

1) Когда приходит GET запрос, то контроллер показываем форму, когда POST, то обрабатывает форму.
2) У бина формы есть две вьюхи - одна сама форма, вторая - куда переходим после удачной обработки формы.
3) В конструкторе контроллера можно вызвать два метода setCommandClass()setCommandName, эти же параметры мы можем указать бину через xml. Как я понимаю это класс и имя объекта, которые получатся после сабмита формы
4) В методе formBackingObject происходит создание объекта, который нам вернет сабмит формы.
5) Метод referenceData нужен для того, чтобы передать на форму какие-то доп данные. Например для создания поста, я в форму должен передать список категорий. Категории получаем именно в этом методе.
6) Метод onSubmit нужен для получения объекта из формы, дальнейшей работы с ним и перехода на successView

Непонятно следующее! Как происходит заполнение объекта данными из формы? Может кто объяснит немного? И еще интересует вопрос использования спринговых тегов, можно даже примерчик сюда кинуть ))

Вот мои мысли по поводу этого контроллера, если где не прав - исправляйте, если где мало сказал - дополняйте smile
PM MAIL   Вверх
Старовъръ
Дата 15.5.2009, 18:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 491
Регистрация: 8.5.2008

Репутация: 1
Всего: 10



Пример:
Код

<form:form commandName="form">
   <form:input path="searchValue"/>
</form>
<form:form commandName="form"> - значит, что работа будет проходить с объектом form(это который backing object). 
<form:input path="searchValue"/> - поле ввода, связанное с полем searchValue объекта form.
Спринг сам заполнит нужное поле, то есть выполнит операцию:
form.setSearchValue("введенное в поле ввода значение")

Добавлено через 6 минут и 33 секунды
Цитата

И еще вопрос, который уже был выше. Итак, пользователь у нас залогинен, ему отображается ссылочка для редактирования поста типа 
http://localhost:8080/blog/editPost.htm?postId=1, он там подредактирует, отсабмитит и все окей. Если пользователь не залогинен, тогда он отсылается на форму логина, это делает сам томкат. Но что делать если пользователь залогинен, но не является автором поста но каким-то чудом узнал такую ссылку и прошел по ней. Открывается та же форма редактирования. Выше предлагались решения, пока решил остановиться на фильтре. Как в фильтре проверить может ли этот пользователь править пост или нет?
Я выше не читал, но так понял, что Spring Security здесь не участвует? Имхо, лучше сразу делать с ним, потратишь время на решение этого вопроса иным способом, а потом снова тратить время на изучение нужного способа.

Это сообщение отредактировал(а) Старовъръ - 15.5.2009, 18:47
PM MAIL WWW   Вверх
garbuz
Дата 15.5.2009, 19:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Старовъръ, спасибо. Не мог бы код jsp скинуть, где эти теги учавствуют и есть какая-нить форма? Если есть конечно smile
PM MAIL   Вверх
Старовъръ
Дата 15.5.2009, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 491
Регистрация: 8.5.2008

Репутация: 1
Всего: 10



Формочка пока совсем еще примитивная: парочка меток, поле ввода, сабмит и табличка:
Код

<%@include file="/WEB-INF/jspf/common/include.jspf" %>
<html>
<head><title><fmt:message key="listUser.title"/></title></head>
<body>
<div>
    <fmt:message key="listUser.title"/>
</div>
<form:form commandName="form">
    <div>
        <fmt:message key="listUesr.search"/> <form:input path="searchValue"/>
        <input type="submit" value="<fmt:message key="common.ok"/> "/>
    </div>
    <div>
        <table width="3">
            <tr>
                <th><fmt:message key="user.login"/></th>
                <th><fmt:message key="user.address.city"/></th>
                <th><fmt:message key="user.rating"/></th>
                <th><fmt:message key="user.pm"/></th>
            </tr>
            <c:if test="${fn:length(users) eq 0}">
                <tr><fmt:message key="listUser.noUsers"/></tr>
            </c:if>
            <c:forEach items="${users}" var="user" varStatus="varStatus">
                <tr>
                    <td><c:out value="${user.login}"/></td>
                    <td><c:out value="${user.address.city}"/></td>
                    <td><c:out value="${user.rating}"/></td>
                    <td><c:url value="htt://PM"/></td>
                </tr>
            </c:forEach>
        </table>
    </div>
</form:form>
</body>
</html>
</html>
Вот backing object:
Код

/**
 * @author ctapobep
 *         Date: 18.04.2009
 *         Time: 12:44:44
 */
public class SearchUsersForm {
    private final String DEFAULT_SORTED_FIELD = "login", DEFAULT_SEARCH_VALUE = "";
    private String backPage, forwardPage;
    private String searchValue;
    private String sortedField;
    private int pageNumber;

    public SearchUsersForm() {
        sortedField = DEFAULT_SORTED_FIELD;
        searchValue = DEFAULT_SEARCH_VALUE;
    }

    public String getBackPage() {
        return backPage;
    }

    public void setBackPage(String backPage) {
        this.backPage = backPage;
    }

    public String getForwardPage() {
        return forwardPage;
    }

    public void setForwardPage(String forwardPage) {
        this.forwardPage = forwardPage;
    }

    public int getPageNumber() {
        return pageNumber;
    }

    public void setPageNumber(int pageNumber) {
        this.pageNumber = pageNumber;
    }

    public String getSearchValue() {
        return searchValue;
    }

    public void setSearchValue(String searchValue) {
        this.searchValue = searchValue;
    }

    public String getSortedField() {
        return sortedField;
    }

    public void setSortedField(String sortedField) {
        this.sortedField = sortedField;
    }
}

Вот контроллер:
Код

import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;
import ru.javatalks.jtchess.model.search.AccountSearchRequest;
import ru.javatalks.jtchess.model.search.SearchRequest;
import ru.javatalks.jtchess.model.service.UserDetailsService;
import ru.javatalks.jtchess.web.form.SearchUsersForm;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @author ctapobep
 *         Date: 18.04.2009
 *         Time: 9:06:13
 */
public class SearchUsersController extends SimpleFormController {

    private UserDetailsService userDetailsService;
    private int recordsLimit;

    public SearchUsersController() {
        setCommandClass(SearchUsersForm.class);
        setCommandName("form");
    }

    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse response,
                                    Object command, BindException e) throws Exception {
        SearchUsersForm form = (SearchUsersForm) command;
        ModelAndView modelAndView = new ModelAndView(getSuccessView());
        modelAndView.addObject("form", form);
        modelAndView.addObject("users", userDetailsService.getUsers(createSearchRequest(form)));
        return modelAndView;
    }

    private SearchRequest createSearchRequest(SearchUsersForm form) {
        AccountSearchRequest searchRequest = new AccountSearchRequest();
        searchRequest.setSearchField("login");
        searchRequest.setSearchValue(form.getSearchValue());
        searchRequest.setSortedField(form.getSortedField());
        String page = form.getBackPage();
        searchRequest.setLimit(recordsLimit);
        return searchRequest;
    }

    @Override
    protected Map referenceData(HttpServletRequest httpServletRequest, Object command,
                                Errors errors) throws Exception {
        SearchUsersForm form = (SearchUsersForm) command;
        Map<String, Object> attr = new HashMap<String, Object>(3);
        attr.put("users", userDetailsService.getUsers(createSearchRequest(form)));
        return attr;
    }

    public void setUsersService(UserDetailsService userDetailsService) {
        this.userDetailsService = userDetailsService;
    }

    public void setRecordsLimit(int recordsLimit) {
        this.recordsLimit = recordsLimit;
    }
}
Правда это у меня еще все в разработке... Спринг не так давно начал изучать.
PM MAIL WWW   Вверх
Старовъръ
Дата 15.5.2009, 19:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 491
Регистрация: 8.5.2008

Репутация: 1
Всего: 10



Кстати, можно использовать и обычные html-теги:
<input type="textarea" name="form.searchValue">
Но если произошла ошибка и страницу нужно наново показать, то в данном случае все введенные данные потеряются. Используя же spring bind tags ничего страшного не произойдет - поля наново заполнятся.
PM MAIL WWW   Вверх
garbuz
Дата 15.5.2009, 22:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Старовъръ, спасибо, буду курить smile завтра )
PM MAIL   Вверх
Kangaroo
Дата 17.5.2009, 01:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Цитата(garbuz @  15.5.2009,  15:34 Найти цитируемый пост)
Метод onSubmit нужен для получения объекта из формы, дальнейшей работы с ним и перехода на successView

Объект из формы передается как параметр в онСабмит.

По всем остальным пунктам - правильно.


--------------------
Lost....
PM MAIL MSN   Вверх
garbuz
Дата 26.5.2009, 19:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Появилось время, сел опять за спринг. Разбираюсь дальше с FormController.
Все вроде бы сделал правильно. 
Код

public class CreatePostController extends SimpleFormController {
    private DAOFacade daoFacade;

    public void setDaoFacade(DAOFacade daoFacade) {
        this.daoFacade = daoFacade;
    }


    @Override
    protected ModelAndView onSubmit(HttpServletRequest request, HttpServletResponse httpServletResponse, Object o, BindException e) throws Exception {
        Post post = (Post) o;
        daoFacade.getPostDAO().create(post);
        return new ModelAndView(getSuccessView());
    }



    @Override
    protected Object formBackingObject(HttpServletRequest request) throws Exception {
        Post post = new Post();
        post.setAuthor(daoFacade.getUserDAO().getByLogin(request.getUserPrincipal().getName()));
        return post;
    }

    @Override
    protected Map referenceData(HttpServletRequest request) throws Exception {
        List<Category> categories = daoFacade.getCategoryDAO().getAll();
        HashMap<String, Object> model = new HashMap<String, Object>();
        model.put("categories", categories);
        return model;
    }
}


Код

<form:form method="POST" commandName="post">
      <table style="width:800px">
        <tr>
            <td>Choose categories</td>
            <td>
                <form:select path="categories" multiple="multiple">
                    <form:options items="${categories}" itemValue="categoryId" itemLabel="categoryName"/>
                </form:select>
            </td>
        </tr>
        <tr>
            <td>Post Name</td>
            <td>
                <form:input path="postName" />

            </td>
        </tr>
        <tr>
            <td>Preview</td>
            <td>
                <form:textarea path="preview" rows="10" cols="40"></form:textarea>
            </td>
        </tr>
        <tr>
            <td>Post Body</td>
            <td>
                <form:textarea path="postBody" rows="10" cols="40"></form:textarea>
                <input type="submit" value="submit">
            </td>
        </tr>
    </table>
    
</form:form>


Код

<bean name="createPost" class="ru.blog.web.controllers.CreatePostController">
        <property name="daoFacade" ref="daoFacade"/>
        <property name="formView" value="newPost"/>
        <property name="successView" value="post"/>
        <property name="commandClass" value="ru.blog.entity.Post"/>
        <property name="commandName" value="post"/>

    </bean>


Странно то, что после сабмита формы я не попадаю в метод onSubmit, у меня проиходит снова вызов formBackingObject и referenceData. Почему?
И еще вопрос. Например я отсабмитился, все нормально. Далее должен следовать переход на success view. А там сразу бы хотелось отобразить новый пост, а этим занимается другой контроллер. Короче надо перейти на другой конторллер и еще ему параметр передать.
PM MAIL   Вверх
garbuz
Дата 27.5.2009, 13:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата из Spring in action
Цитата

The onSubmit() method handles the form submission (an HTTP POST
request) by passing the command object (which is an instance of Rant) to the
addRant() method of the injected RantService reference.

Не понимаю, почему у меня после сабмита формы не вызывается этот метод. Метод пост в форме указан явно, commaddName тоже поменял с post на newPost, даже атрибут action явно указал, но все равно попадаю в formBackingObject()
PM MAIL   Вверх
Kangaroo
Дата 27.5.2009, 14:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Цитата(garbuz @  26.5.2009,  19:10 Найти цитируемый пост)
Странно то, что после сабмита формы я не попадаю в метод onSubmit, у меня проиходит снова вызов formBackingObject и referenceData. Почему?

formBackingObject  вызывается всегда.
А вот referenceData не должен. Может какие-то ошибки валидации и Спринг обратно показывает форму?

Попробуй:
1. Пройтись дебагом. Посмотри метод processFormSubmission , там как раз вызывается onSubmit
2. Посмотри полный лог Спринга (левел DEBUG).


Цитата(garbuz @  26.5.2009,  19:10 Найти цитируемый пост)
А там сразу бы хотелось отобразить новый пост, а этим занимается другой контроллер. Короче надо перейти на другой конторллер и еще ему параметр передать. 

В онсабмите:
Код

return new ModelAndView(new RedirectView("куда шагать"));



--------------------
Lost....
PM MAIL MSN   Вверх
garbuz
Дата 27.5.2009, 15:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
formBackingObject  вызывается всегда

Почему? Разве не только перед отображением формы? Насколько я понимаю, в этом методе создается объект, который будет запонятсья данными из формы, и его же мы получам в onSubmit как параметр. Зачем еще раз вызов метода formBackingObject(), зачем нам второй объект?
Если я неверно понимаю ситуацию - прошу объяснить smile


Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
А вот referenceData не должен. Может какие-то ошибки валидации и Спринг обратно показывает форму?

В методе processFormSubmission поставил брейкпоинт и обнаружил эксепшн! Там что-то не то с валидацией! Буду разбираться что. Хотя валидация явно пока еще не прикручена.



Цитата(Kangaroo @  27.5.2009,  14:26 Найти цитируемый пост)
Посмотри полный лог Спринга (левел DEBUG).

А где это можно глянуть?



PM MAIL   Вверх
garbuz
Дата 28.5.2009, 00:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Как выяснилось, ошибка в том, что в объект в поле типа Set попадает строка. Есть на странице множественный select, как выбранные элементы списка превратить в набор и отдать его объекту?
PM MAIL   Вверх
garbuz
Дата 28.5.2009, 16:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Мучаю дальше SimpleFormController (если разберусь, то можно и статейку в faq набросать smile )
Сейчас стоит задача забиндить коллекцию в объект. Нашел в инете примерчик
Цитата

This method allows you to register custom editors for certain fields of your command class. For instance, you will be able to transform Date objects into a String pattern and back, in order to allow your JavaBeans to have Date properties and still be able to set and display them in an HTML interface. 


Код

 @Override
    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder) throws Exception {
        binder.registerCustomEditor(Set.class, "categories", new CustomCollectionEditor(Set.class){
            protected Object convertElement(Object element){
                if (element != null){
                    Long categoryId = Long.parseLong(element.toString());
                    Category category = daoFacade.getCategoryDAO().getById(categoryId);
                    return category;                   
                }
                return null;
            }
        });
    }

Правда не совсем понятно, как это все работает. 
Итак, как происходит процесс у меня, судя по дебаггеру.
1) Вызывается formBackingObject(). Кстати, что за свойство sessionForm(true/false). Я чего-то так и не понял, за что отвечает? Поставил значение true и после сабмита метод formBackingObject() больше не стал вызываться, то что я и хотел.
2) Вызов initBinder, где сначала происходит только регистрация customEditor, как я понял
3) Список категорий получаю в методе referenceData()
4) Снова initBinder, но в этом методе уже происходит вызов метода converElement, в качестве объекта element приходит id категории, т.е. просто число. Вот тут непонятно? Откуда он взялся? Из тех данных что поулчены в referenceData? Или откуда еще? Причем приходит не один id, а итерируюстя id всей коллекции категории, да. То что нужно. Но если у меня всего 4 категории, то вызов converElement происходит 8 раз, по два раза для id каждой категории. Почему?
5) Отображается форма.
6) Заполняю, сабмичу.
7) Снова вызов initBinder, в качестве element приходит "" (об этом позже)
8) Появляется ексепшн
9) Вызов processFormSubmission()
10) Снова вызов referenceData
11)  Снова вызов initBinder как в пункте 4
12) И так по кругу....

Короче вот такая ерунда. Непонятно зачем метод initBinder вызывается столько раз? Мне кажется, что он должен вызываться один раз после сабмита, получать значение из формы и вытаскивать объект по этому значению. Короче каша в голове. Кто объяснит, буду признателен.

и еще один момент на счет jsp
Код

<form:select path="categories" multiple="multiple">
     <form:options items="${categories}" itemValue="categoryId" itemLabel="categoryName"/>
</form:select>

catefories - это коллекция, которую получаю в referenceData.
HTML на странице выглядит так 
Код

<td>Choose categories</td>

            <td>
                <select id="categories" name="categories" multiple="multiple">
                    
                        <option value="">Ajax</option>
                    
                        <option value="">Java</option>
                    
                        <option value="">Linux</option>
                    
                        <option value="">Новости</option>
                    
                    
                </select><input type="hidden" name="_categories" value="1"/>

            </td>


Значение атрибута value пустые, поэтому и в initBinder после сабмита приходит пустая строка. Почему пустые, пример select и options взял с офф референса. 

Буду рад любой помощи smile
PM MAIL   Вверх
Kangaroo
Дата 29.5.2009, 01:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Ого, сколько проблем  smile  Сегодня не успеваю, завтра утром постараюсь помочь.


--------------------
Lost....
PM MAIL MSN   Вверх
garbuz
Дата 29.5.2009, 02:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(Kangaroo @  29.5.2009,  01:37 Найти цитируемый пост)
го, сколько проблем  smile  Сегодня не успеваю, завтра утром постараюсь помочь. 

Жду! А то мне даже не уснуть smile
PM MAIL   Вверх
Kangaroo
Дата 29.5.2009, 15:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Цитата(garbuz @  27.5.2009,  15:22 Найти цитируемый пост)
Зачем еще раз вызов метода formBackingObject(), зачем нам второй объект?

Это связано с параметром sessionForm.
Зачем этот флаг нужен: если он установлен, то комманд обжект хранится в в сессии, а не в реквесте. Это нужно, например, для визард форм, одна форма на несколько страниц.
Вот для этого и в вызывается formBackingObject второй раз (при "ПОСТ") - чтобы или вытянуть из сессии уже существующий комманд обжект, или сделать новый.
Поставь его в false у себя.

Цитата(garbuz @  27.5.2009,  15:22 Найти цитируемый пост)
А где это можно глянуть?

Если у тебя log4j подключен, поставь левел DEBUG для пакета org.springframework

Цитата(garbuz @  28.5.2009,  00:53 Найти цитируемый пост)
Есть на странице множественный select, как выбранные элементы списка превратить в набор и отдать его объекту? 

Посмотри тут, хотя у тебя вроде похоже сделано.


Цитата(garbuz @  28.5.2009,  16:29 Найти цитируемый пост)
Непонятно зачем метод initBinder вызывается столько раз? Мне кажется, что он должен вызываться один раз после сабмита, получать значение из формы и вытаскивать объект по этому значению.

Из-за ошибок бинда получается такой бардак, попробуй их исправить и посмотрим дальше.

По селекту - вроде все правильно. Ты точно айдишники передаешь? Геттеры/сеттеры правильные?


И еще.
1. Почему не возьмешь книгу по Спрингу, например "Spring Live". Там вроде это хорошо объясняется.
2. Почему не смотришь исходники Спринга? Для чего их открытыми держат? smile Там и увидишь всю логику обработки запроса.


--------------------
Lost....
PM MAIL MSN   Вверх
garbuz
Дата 29.5.2009, 18:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Kangaroo, во-первых, спасибо большое за ответы!  smile 

Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)
Это связано с параметром sessionForm.
Зачем этот флаг нужен: если он установлен, то комманд обжект хранится в в сессии, а не в реквесте. Это нужно, например, для визард форм, одна форма на несколько страниц.
Вот для этого и в вызывается formBackingObject второй раз (при "ПОСТ") - чтобы или вытянуть из сессии уже существующий комманд обжект, или сделать новый.
Поставь его в false у себя.

В принципе я все понял из твоего объяснения, но если я ставлю 
Код

setSessionForm(true);

то у меня formBackingObject вызывается один раз до сабмита формы, как я хотел. Т.е. как я понимаю, в этом методе, создаем объект, который будем заполнять данными из формы. Соответственно после сабмита нам надо получить его уже с данными. Если же поставить 
Код

setSessionForm(false);

то метод вызывается еще раз после сабмита, создается новый комманд обджект, а это мне не надо. Или я опять что-то неверно понял?


Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)
Если у тебя log4j подключен, поставь левел DEBUG для пакета org.springframework

Нет, пока без него. Стыдно товарищи, но я им еще вообще не пользовался.

Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)

Из-за ошибок бинда получается такой бардак, попробуй их исправить и посмотрим дальше.
По селекту - вроде все правильно. Ты точно айдишники передаешь? Геттеры/сеттеры правильные?

Исправил. Забил на спринговые теги, оставил только селект.
Код

<form:select path="categories" multiple="multiple">
       <c:forEach items="${cats}" var="cat">
               <option value="${cat.categoryId}"><c:out value="${cat.categoryName}"/></option>
       </c:forEach>
</form:select>

Так id выводится, все работает, биндер соответственно тоже отрабатывает нормально. С этим вроде разобрался smile
Еще вопрос по спринговым тегам. В примерах встречал <spring:bind> это как я понимаю в старых версиях спринга было, сейчас <form:. Верно? Или путаю? smile


Цитата(Kangaroo @  29.5.2009,  15:29 Найти цитируемый пост)

И еще.
1. Почему не возьмешь книгу по Спрингу, например "Spring Live". Там вроде это хорошо объясняется.
2. Почему не смотришь исходники Спринга? Для чего их открытыми держат? smile Там и увидишь всю логику обработки запроса. 

Книгу нашел, буду глядеть если что. В исходникики тоже постараюсь заглянуть smile 

Следующие вопросы )
1)Хочу сделать отдельное поле для ввода тегов через запятую, набор тегов тоже представляет собой коллекцию, но в итоге это будет выглядеть не как массив, как в случае с множественным селектом, а как простая строка. Какой биндер выбрать, чтобы на основе этой строки выбралась/создалась коллекция тегов. 
2)Так же туда же хочу запихать чекбокс, там пользователь отмечает черновик это или же нет. Ну понятно что я имею ввиду. Тоже вопрос какой биндер выбрать, чтобы вытащить объетк типа Status и назначить его новому посту.

Спасибо!

PM MAIL   Вверх
Kangaroo
Дата 29.5.2009, 23:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Цитата(garbuz @  29.5.2009,  18:02 Найти цитируемый пост)
то метод вызывается еще раз после сабмита, создается новый комманд обджект, а это мне не надо. Или я опять что-то неверно понял?

Он берется пустой и заполняется (биндится) значениями с формы. Если бы у тебя сессонФорм = тру, то брался бы не пустой, а полученный из сессии (может в нем уже есть данные с предыдущих форм-страниц).


Цитата(garbuz @  29.5.2009,  18:02 Найти цитируемый пост)
В примерах встречал <spring:bind> это как я понимаю в старых версиях спринга было, сейчас <form:. Верно? Или путаю? 

Я с этим не работал, но думаю что в <form: используется <spring:bind>. Он просто более нижнего уровня.

По вопросам:
1. Не знаю такого, напиши сам.

2. Зачем Status, если можно boolean и и стандартный биндер checkbox-boolean.


--------------------
Lost....
PM MAIL MSN   Вверх
garbuz
Дата 31.5.2009, 03:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)

Он берется пустой и заполняется (биндится) значениями с формы. Если бы у тебя сессонФорм = тру, то брался бы не пустой, а полученный из сессии (может в нем уже есть данные с предыдущих форм-страниц).

Кажется начинаю понимать. Сам объект, который мне собственно и нужен, формируется два раза - до и после сабмита. Если я ставлю значение false, то мой объект будет тот, который уже создается после сабмита, и в который потом заносятся значения. А тот что был получен до сабмита, его схавает сборщик мусора.


Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)
1. Не знаю такого, напиши сам.

вот. В принципе то, что я хотел (для реализации пришлось лезть в исходники спринга  smile )
Код

binder.registerCustomEditor(Set.class, "tags", new CustomCollectionEditor(Set.class) {
            public void setValue(Object value) {
                if (value != null) {
                    String str = value.toString();
                    String[] tags = str.trim().split(",");
                    Collection collection = Arrays.asList(tags);
                    super.setValue(collection);
                }
            }

            protected Object convertElement(Object element) {
                if (element != null) {
                    String tagName = element.toString();
                    Tag tag;
                    try {
                        tag = daoFacade.getTagDAO().getByName(tagName);
                        return tag;
                    } catch (IndexOutOfBoundsException e) {
                        tag = new Tag();
                        tag.setTagName(tagName);
                        tag.setPosts(new HashSet());
                        daoFacade.getTagDAO().create(tag);
                        return tag;
                    }
                }
                return null;
            }

        });


Вроде даже как работает smile



Цитата(Kangaroo @  29.5.2009,  23:46 Найти цитируемый пост)
2. Зачем Status, если можно boolean и и стандартный биндер checkbox-boolean. 

У поста планируется не два статуса, а больше. Так что обычный булеан тут не прокатит. Буду тоже извращаться и писать что-нибудь подходящее. 

Еще раз спасибо! smile

PM MAIL   Вверх
garbuz
Дата 1.6.2009, 15:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Вот биндер для статуса, решил использовать радиобаттоны
Код

<td>Draft     : <form:radiobutton path="status" value="inactive"/> </td>
<td>Publish : <form:radiobutton path="status" value="active"/></td>

Сам кастом едитор
Код

binder.registerCustomEditor(Status.class, "status", new PropertyEditorSupport(){
            public void setAsText(String text){
                if (text instanceof String){
                    if (text.equals("inactive")){
                        Status status = daoFacade.getStatusDAO().getByName("inactive");
                        setValue(status);
                    } else if (text.equals("active")){
                        Status status = daoFacade.getStatusDAO().getByName("active");
                        setValue(status);
                    }

                }
            }

        });

Вопрос вот в чем. Когда Происход регистрация биндера, до показа формы, то сразу вызывается метод setTextAs, причем вызывается два раза с двумя значениями радиобаттнов, т.е. по идее из базы дергаются два объекта. После сабмита этот метод вызывается снова, но уже один раз с тем значением, которое мы выбрали на странице. Вытаскивается нужный объект типа Status. Почему спринг перед сабмитом дергает этод метод и вытаскивает "лишние объекты"?

PM MAIL   Вверх
garbuz
Дата 2.6.2009, 01:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Начал прикручивать валидацию!  smile 
С текстовыми полями все ясно вроде бы, переопределяем метод getText:
Код

 public String getAsText() {
                Object value = getValue();
                if (value != null) {
                    Set<Tag> tags = (Set<Tag>) getValue();
                    StringBuilder builder = new StringBuilder();
                    for (Tag c : tags) {
                        if (c instanceof Tag) {
                            String tagName = c.getTagName();
                            System.out.println("tag name = " + tagName);
                            builder.append(tagName + ", ");
                        }
                    }
                    return builder.toString();
                }
                return null;
            }

Этот метод, возвращает строку из имени объектов коллекции тегов. 
А что делать с радиобаттонами и селектом? Как у них на странице засетить выбранные ранее значения?

PS. Вопрос по методу выше, почему метод вызывается еще несколько раз(а именно 4) до показы формы, потом после сабмита и после валидации. Неужели так и должно быть?
PM MAIL   Вверх
Старовъръ
Дата 2.6.2009, 12:01 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 491
Регистрация: 8.5.2008

Репутация: 1
Всего: 10



1. Ммм.. а как валидация происходит? Не вижу тега <form:errors...>
2. <spring:bind> - это устаревшая вещь.
3. Почему бы не разбивать вопросы по разным темам? Название темы должно отражать ее суть и в одной теме желательно должен звучать один вопрос. Другим будет легче искать уже отвеченные темы, если их разбивать по отдельным вопросам.

Это сообщение отредактировал(а) Старовъръ - 2.6.2009, 12:03
PM MAIL WWW   Вверх
garbuz
Дата 2.6.2009, 12:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(Старовъръ @  2.6.2009,  12:01 Найти цитируемый пост)
1. Ммм.. а как валидация происходит? Не вижу тега <form:errors...>
2. <spring:bind> - это устаревшая вещь

<form:errors...> на странице
Код

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ include file="include.jsp" %>
<html>
<head><title>New Post</title></head>
<body>
<form:form method="POST" commandName="newPost" action="createPost.htm">
    <table style="width:800px">
        <tr>
            <td>Choose categories</td>
            <td>
                <form:select path="categories" multiple="multiple">
                    <c:forEach items="${cats}" var="cat">
                        <option value="${cat.categoryId}"><c:out value="${cat.categoryName}"/></option>
                    </c:forEach>
                </form:select>
            </td>
            <td><form:errors path="categories" cssStyle="color:red;"/></td>
        </tr>
        <tr>
            <td>Post Name</td>
            <td>
                <form:input path="postName" />
            </td>
            <td><form:errors path="postName" cssStyle="color:red"/></td>
        </tr>
        <tr>
            <td>Preview</td>
            <td>
                <form:textarea path="preview" rows="10" cols="40"></form:textarea>
            </td>
            <td><form:errors path="preview" cssStyle="color:red;"/></td>
        </tr>
        <tr>
            <td>Post Body</td>
            <td>
                <form:textarea path="postBody" rows="10" cols="40"></form:textarea>
            </td>
            <td><form:errors path="postBody" cssStyle="color:red;"/></td>
        </tr>
          <tr>
            <td>Tags</td>
            <td>
                <form:input path="tags"/>
            </td>
            <td><form:errors path="tags" cssStyle="color:red;"/></td>
        </tr>
         <tr>
             <td>Status</td>
             <td>Draft   : <form:radiobutton path="status" value="inactive"/><br>
                 Publish : <form:radiobutton path="status" value="active"/></td>
             <td><form:errors path="status" cssStyle="color:red;"/></td>
         </tr>
          <tr>
             <td></td>
             <td></td>
             <td><input type="submit" value="submit"></td>
         </tr>
    </table>

</form:form>
</body>
</html>


А про <spring:bind я так и думал, что это устаревшая версия. Вопросы остаются открытыми:
Цитата

А что делать с радиобаттонами и селектом? Как у них на странице засетить выбранные ранее значения?

PS. Вопрос по методу выше, почему метод вызывается еще несколько раз(а именно 4) до показы формы, потом после сабмита и после валидации. Неужели так и должно быть?

PM MAIL   Вверх
garbuz
Дата 2.6.2009, 13:27 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Еще вопрос.
Есть контроллер, который просто отображает какой-либо пост. На странице внизу есть форма для комментарев. Т.е. сама форма отображается все тем же контроллером. Какого типа должен быть контроллер, который обрабатывает комментарии? 
1)Если он будет предком SimpeFormController, тогда получается, что сама форма отображается другим контроллером. Или это не важно? Можно обойтись только POST запросом и все. Но с другой стороны если прикручивать валидацию и будет ошибка, то надо как-то снова отобразить эту страницу с формой, но этим же занимается другой контроллер.
2) Сделать предком AbstractController и просто вытаскивать данные из запроса. Но тогда мы теряем валидацию и прочие прелести SimpleFormController'a. 

Как в этом случае лучше поступить?
PM MAIL   Вверх
garbuz
Дата 2.6.2009, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Найдено рещение для радиобаттонов. Надо тоже было переопределить метод getAsText, который должен возвращать строку, подобно той, которая прописана у радиобаттонв на странице в атрибуте value, тогда после сабмита и возврата на форму, этот радиобаттно становится выделенным smile 

Остался множественный селект :(
PM MAIL   Вверх
garbuz
Дата 3.6.2009, 00:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Решил пока оставить select в покое.
Перефразирую свой вопрос выше более понятно.

Есть PostController, который в качестве параметра принимает id поста, выбирает пост с комментариями из базы и вызывает вьюху, где все эти данные отображаются. Внизу страницы есть текстовое поле для комментариев. Так вот, какого типа должен быть контроллер, который будет добавлять эти комментарии?
Я вижу два варианта.
1) Это будет класс, расширяющий SipmleFormController
2) Это будет класс, расширяющий AbstractController

Если это первый вариант, то как мне в форме, которая будет отображаться другой вьюхой (та что упоминалась выше) указать поля для биндинга данных, что будет в случае если не пройдет валидация и снова потребуется отобразить форму - соответственно нужен GET запрос и нужно снова отобразить эту страницу с формой? Этим же занмиается Postcontroller. короче вот такая ситуация.

Если это второй вариант, то тут как мне кажется все проще. Получаем из запроса текст как параметр, валидируем его собственными средтвами, снова отображаем эту страничку путем редиректа на PostController. Хотя если возникнет ошибка при валидации ее придется как-то передать сквозь редирект. 

Кто что посоветует? Может есть более красивые и правильные решения? Надеюсь, что понятно обрисовал всю ситуацию smile
PM MAIL   Вверх
Старовъръ
Дата 4.6.2009, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 491
Регистрация: 8.5.2008

Репутация: 1
Всего: 10



Нет, не понятно) Если на странице есть инпуты, используется SimpleFormController. Он же расширяет AbstractController, посему ты и так, и так будешь использовать AbstractController smile

Это сообщение отредактировал(а) Старовъръ - 4.6.2009, 14:09
PM MAIL WWW   Вверх
garbuz
Дата 4.6.2009, 18:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 677
Регистрация: 22.1.2008

Репутация: 8
Всего: 11



Цитата(Старовъръ @  4.6.2009,  14:09 Найти цитируемый пост)
Нет, не понятно) Если на странице есть инпуты, используется SimpleFormController. Он же расширяет AbstractController, посему ты и так, и так будешь использовать AbstractControlle

Мда, что-то я как-то затупил, почему сразу не использовать один контроллер для отображения поста и создания комментраиев. Сам пост и имеющиеся комментарии можно выбирать в методе referenceData как доп данные. А сам новый комментарий будет как commandObject.
Старовъръ, спасибо за наводку  smile 
PM MAIL   Вверх
Omut
Дата 4.6.2009, 22:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 15
Регистрация: 22.12.2005

Репутация: нет
Всего: нет



Цитата(Старовъръ @ 2.6.2009,  12:01)

3. Почему бы не разбивать вопросы по разным темам? Название темы должно отражать ее суть и в одной теме желательно должен звучать один вопрос. Другим будет легче искать уже отвеченные темы, если их разбивать по отдельным вопросам.

Для других обучающихся лучше когда в одной теме. Суть темы предельно проста - обучение Spring'у. Так держать, гарбуз!
PM MAIL   Вверх
Kangaroo
Дата 4.6.2009, 23:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


AA - Aussie Animal
****


Профиль
Группа: Участник Клуба
Сообщений: 2042
Регистрация: 7.10.2006
Где: US

Репутация: 14
Всего: 104



Цитата(Omut @  4.6.2009,  22:48 Найти цитируемый пост)
Для других обучающихся лучше когда в одной теме. Суть темы предельно проста - обучение Spring'у. Так держать, гарбуз! 

Не-а.

В одной теме удобно - это когда туториал. Читаешь и разбираешься.
А вот разные проблемы/вопросы решать лучше в разных темах, чтобы другим легче было их найти и прочитать решение (а не искать свою проблему среди Н-страниц)


Вот как-то так.  smile 


--------------------
Lost....
PM MAIL MSN   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java EE (J2EE) и Spring | Следующая тема »


 




[ Время генерации скрипта: 0.2147 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.