Параметры расширенного подстановочного знака, не найденные в методе 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 и ответе Андреа.

Другие вопросы по тегам