Параметры расширенного подстановочного знака, не найденные в методе Prepare()
Из документации: Расширенные сопоставления подстановочных знаков Struts2:
Расширенные шаблоны
Начиная с 2.1.9+, регулярные выражения могут быть определены в имени действия. Чтобы использовать эту форму подстановочного знака, должны быть установлены следующие константы:
<constant name="struts.enable.SlashesInActionNames" value="true"/> <constant name="struts.mapper.alwaysSelectFullNamespace" value="false"/> <constant name="struts.patternMatcher" value="regex" />
Регулярные выражения могут быть в двух формах, самый простой
{FIELD_NAME}
в этом случае поле сFIELD_NAME
в действии будет заполнен сопоставленный текст, например:<package name="books" extends="struts-default" namespace="/"> <action name="/{type}/content/{title}" class="example.BookAction"> <result>/books/content.jsp</result> </action> </package>
В этом примере, если URL
/fiction/content/Frankenstein
запрашивается, поле BookAction "type
"будет установлен в"fiction
"и поле"title
"будет установлен в"Frankenstein
".
Это абсолютно здорово, и работает нормально, если вы читаете эти переменные в обычном методе Action, например execute()
,
Если вы попытаетесь прочитать их с prepare()
метод, они нулевые, потому что PrepareInterceptor
запускается раньше, чем другие перехватчики, отвечающие за настройку параметров; Обычный способ решить эту проблему - использовать подходящий Interceptor Stack для получения параметров, уже заполненных при выполнении prepare()
метод...
Из документации: ParamsPrepareParamStack
<!-- An example of the paramsPrepareParams trick. This stack
is exactly the same as the defaultStack, except that it
includes one extra interceptor before the prepare interceptor:
the params interceptor.
This is useful for when you wish to apply parameters directly
to an object that you wish to load externally (such as a DAO
or database or service layer), but can't load that object
until at least the ID parameter has been loaded. By loading
the parameters twice, you can retrieve the object in the
prepare() method, allowing the second params interceptor to
apply the values on the object. -->
Это прекрасно работает для параметров, поступающих со страницы, но не работает для параметров, установленных Advanced Wildcards. Они все еще нулевые.
Как решить эту проблему?
2 ответа
Эти параметры устанавливаются не ParametersInterceptor (например, из JSP), а StaticParametersInterceptor.
Чтобы они заполнили prepare()
метод, тот же трюк paramsPrepareParamsStack
должны быть применены.
Поскольку нет стека перехватчиков, который делает это " из коробки", вы должны определить его.
Начиная с defaultStack
мы должны создать стек следующим образом:
<interceptor-stack name="allYourParamsAreBelongToUsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="i18n"/>
<!-- THE TRICK: NOW PREPARE() WILL FIND EVERYTHING SET -->
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<!-- END OF THE TRICK -->
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="scopedModelDriven"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="checkbox"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params">
<param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
</interceptor-ref>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="debugging"/>
</interceptor-stack>
Замечания: ActionMappingParams
не требуется, просто включен для использования в будущем.
Пожалуйста, прокомментируйте, если вы обнаружите любую проблему, связанную с этим. AFAIK, это работает без нареканий.
@ Андреа решение хорошее, но у меня были некоторые проблемы:
- staticParams, actionMappingParams и params interceptor вызываются дважды
- загрузка файла прервана
Итак, я использую этот действительно закрытый стек вместо:
<interceptor-stack name="myOwnParamsPrepareParamsStack">
<interceptor-ref name="exception"/>
<interceptor-ref name="alias"/>
<interceptor-ref name="i18n"/>
<!-- Needed for advanced wildcards parameters setted before prepare() -->
<interceptor-ref name="fileUpload"/>
<interceptor-ref name="staticParams"/>
<interceptor-ref name="actionMappingParams"/>
<interceptor-ref name="params"/>
<!-- /Needed for advanced wildcards parameters setted before prepare() -->
<interceptor-ref name="checkbox"/>
<interceptor-ref name="datetime"/>
<interceptor-ref name="multiselect"/>
<interceptor-ref name="servletConfig"/>
<interceptor-ref name="prepare"/>
<interceptor-ref name="chain"/>
<interceptor-ref name="modelDriven"/>
<interceptor-ref name="conversionError"/>
<interceptor-ref name="validation">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
<interceptor-ref name="workflow">
<param name="excludeMethods">input,back,cancel,browse</param>
</interceptor-ref>
</interceptor-stack>
Кстати, это основано на стандартном paramsPrepareParamsStack и ответе Андреа.